@atlaskit/editor-plugin-block-controls 2.7.1 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @atlaskit/editor-plugin-block-controls
2
2
 
3
+ ## 2.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#154648](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/154648)
8
+ [`7224898e37116`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/7224898e37116) -
9
+ Don't create node decs within tables if nested dnd not enabled
10
+ - [#154380](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/154380)
11
+ [`e045dd3ba95bd`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/e045dd3ba95bd) -
12
+ ED-25281 implement DnD API for appending to a layout
13
+
14
+ ### Patch Changes
15
+
16
+ - [#154186](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/154186)
17
+ [`5c316170d29dd`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/5c316170d29dd) -
18
+ Bump @atlaskit/adf-schema to 42.3.1
19
+ - Updated dependencies
20
+
21
+ ## 2.8.0
22
+
23
+ ### Minor Changes
24
+
25
+ - [`ed73a2928e080`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/ed73a2928e080) -
26
+ ED-25375 fix drop target v2 end position drop area
27
+
3
28
  ## 2.7.1
4
29
 
5
30
  ### Patch Changes
@@ -5,13 +5,13 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.moveToLayout = void 0;
7
7
  var _model = require("@atlaskit/editor-prosemirror/model");
8
- var _consts = require("../ui/consts");
8
+ var _consts = require("../consts");
9
+ var _consts2 = require("../ui/consts");
9
10
  var createNewLayout = function createNewLayout(schema, layoutContents) {
10
- // TODO update with constant
11
- if (layoutContents.length === 0 || layoutContents.length > 5) {
11
+ if (layoutContents.length === 0 || layoutContents.length > _consts.MAX_LAYOUT_COLUMN_SUPPORTED) {
12
12
  return null;
13
13
  }
14
- var width = _consts.DEFAULT_COLUMN_DISTRIBUTIONS[layoutContents.length];
14
+ var width = _consts2.DEFAULT_COLUMN_DISTRIBUTIONS[layoutContents.length];
15
15
  if (!width) {
16
16
  return null;
17
17
  }
@@ -19,11 +19,12 @@ var createNewLayout = function createNewLayout(schema, layoutContents) {
19
19
  layoutSection = _ref.layoutSection,
20
20
  layoutColumn = _ref.layoutColumn;
21
21
  try {
22
- var layoutSectionNode = layoutSection.createChecked(undefined, _model.Fragment.fromArray(layoutContents.map(function (layoutContent) {
22
+ var layoutContent = _model.Fragment.fromArray(layoutContents.map(function (layoutContent) {
23
23
  return layoutColumn.createChecked({
24
24
  width: width
25
25
  }, layoutContent);
26
- })));
26
+ }));
27
+ var layoutSectionNode = layoutSection.createChecked(undefined, layoutContent);
27
28
  return layoutSectionNode;
28
29
  } catch (error) {
29
30
  // TODO analytics
@@ -34,10 +35,16 @@ var moveToLayout = exports.moveToLayout = function moveToLayout(api) {
34
35
  return function (from, to, position) {
35
36
  return function (_ref2) {
36
37
  var tr = _ref2.tr;
38
+ // unable to drag a node to itself.
39
+ if (from === to) {
40
+ return tr;
41
+ }
37
42
  var _ref3 = tr.doc.type.schema.nodes || {},
38
43
  layoutSection = _ref3.layoutSection,
39
44
  layoutColumn = _ref3.layoutColumn,
40
45
  doc = _ref3.doc;
46
+ var _ref4 = tr.doc.type.schema.marks || {},
47
+ breakout = _ref4.breakout;
41
48
 
42
49
  // layout plugin does not exist
43
50
  if (!layoutSection || !layoutColumn) {
@@ -51,26 +58,48 @@ var moveToLayout = exports.moveToLayout = function moveToLayout(api) {
51
58
  }
52
59
  var $from = tr.doc.resolve(from);
53
60
 
54
- // invalid from position
55
- if (!$from.nodeAfter) {
61
+ // invalid from position or dragging a layout
62
+ if (!$from.nodeAfter || $from.nodeAfter.type === layoutSection) {
56
63
  return tr;
57
64
  }
58
65
  var toNode = $to.nodeAfter;
59
66
  var fromNode = $from.nodeAfter;
60
- var fromNodeEndPos = from + fromNode.nodeSize;
61
- var toNodeEndPos = to + toNode.nodeSize;
62
- if ($to.nodeAfter.type !== layoutSection) {
63
- var layoutContents = position === 'left' ? [fromNode, toNode] : [toNode, fromNode];
67
+
68
+ // remove breakout from node;
69
+ if (breakout && $from.nodeAfter && $from.nodeAfter.marks.some(function (m) {
70
+ return m.type === breakout;
71
+ })) {
72
+ tr = tr.removeNodeMark(from, breakout);
73
+ }
74
+ if ($to.nodeAfter.type === layoutSection) {
75
+ var existingLayoutNode = $to.nodeAfter;
76
+ if (existingLayoutNode.childCount < _consts.MAX_LAYOUT_COLUMN_SUPPORTED) {
77
+ var toPos = position === 'left' ? to + 1 : to + existingLayoutNode.nodeSize - 1;
78
+ tr = tr.insert(toPos,
79
+ // resolve again the source node after node updated (remove breakout marks)
80
+ layoutColumn.create(null, tr.doc.resolve(from).nodeAfter));
81
+ var mappedFrom = tr.mapping.map(from);
82
+ var mappedFromEnd = mappedFrom + fromNode.nodeSize;
83
+ tr = tr.delete(mappedFrom, mappedFromEnd);
84
+ return tr;
85
+ }
86
+ return tr;
87
+ } else {
88
+ // resolve again the source node after node updated (remove breakout marks)
89
+ var newFromNode = tr.doc.resolve(from).nodeAfter;
90
+ if (!newFromNode) {
91
+ return tr;
92
+ }
93
+ var layoutContents = position === 'left' ? [newFromNode, toNode] : [toNode, newFromNode];
64
94
  var newLayout = createNewLayout(tr.doc.type.schema, layoutContents);
65
95
  if (newLayout) {
66
- tr.delete(from, fromNodeEndPos);
96
+ tr = tr.delete(from, from + fromNode.nodeSize);
67
97
  var mappedTo = tr.mapping.map(to);
68
- tr.delete(mappedTo, toNodeEndPos);
69
- tr.insert(mappedTo, newLayout); // insert the content at the new position
98
+ tr = tr.delete(mappedTo, mappedTo + toNode.nodeSize);
99
+ tr = tr.insert(mappedTo, newLayout); // insert the content at the new position
70
100
  }
71
101
  return tr;
72
102
  }
73
- return tr;
74
103
  };
75
104
  };
76
105
  };
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.nodeDecorations = exports.getNodeAnchor = exports.findNodeDecs = exports.findHandleDec = exports.findDropTargetDecs = exports.emptyParagraphNodeDecorations = exports.dropTargetDecorations = exports.dragHandleDecoration = exports.createDropTargetDecoration = exports.TYPE_NODE_DEC = exports.TYPE_HANDLE_DEC = exports.TYPE_DROP_TARGET_DEC = void 0;
7
+ exports.shouldDescendIntoNode = exports.nodeDecorations = exports.getNodeAnchor = exports.findNodeDecs = exports.findHandleDec = exports.findDropTargetDecs = exports.emptyParagraphNodeDecorations = exports.dropTargetDecorations = exports.dragHandleDecoration = exports.createDropTargetDecoration = exports.TYPE_NODE_DEC = exports.TYPE_HANDLE_DEC = exports.TYPE_DROP_TARGET_DEC = void 0;
8
8
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
9
9
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
10
10
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
@@ -48,6 +48,12 @@ var getNodeMargins = function getNodeMargins(node) {
48
48
  }
49
49
  return _consts.nodeMargins[nodeTypeName] || _consts.nodeMargins['default'];
50
50
  };
51
+ var shouldCollapseMargin = function shouldCollapseMargin(prevNode, nextNode) {
52
+ if (((prevNode === null || prevNode === void 0 ? void 0 : prevNode.type.name) === 'mediaSingle' || (nextNode === null || nextNode === void 0 ? void 0 : nextNode.type.name) === 'mediaSingle') && (prevNode === null || prevNode === void 0 ? void 0 : prevNode.type.name) !== (nextNode === null || nextNode === void 0 ? void 0 : nextNode.type.name)) {
53
+ return false;
54
+ }
55
+ return true;
56
+ };
51
57
  var getGapAndOffset = function getGapAndOffset(prevNode, nextNode, parentNode) {
52
58
  if (!prevNode && nextNode) {
53
59
  // first node
@@ -63,7 +69,7 @@ var getGapAndOffset = function getGapAndOffset(prevNode, nextNode, parentNode) {
63
69
  }
64
70
  var top = getNodeMargins(nextNode).top || 4;
65
71
  var bottom = getNodeMargins(prevNode).bottom || 4;
66
- var gap = Math.max(top, bottom);
72
+ var gap = shouldCollapseMargin(prevNode, nextNode) ? Math.max(top, bottom) : top + bottom;
67
73
  var offset = top - gap / 2;
68
74
  if ((prevNode === null || prevNode === void 0 ? void 0 : prevNode.type.name) === 'mediaSingle' && (nextNode === null || nextNode === void 0 ? void 0 : nextNode.type.name) === 'mediaSingle') {
69
75
  offset = -offset;
@@ -140,8 +146,7 @@ var createDropTargetDecoration = exports.createDropTargetDecoration = function c
140
146
  offset = _getGapAndOffset.offset;
141
147
  element.style.setProperty(_dropTargetV.EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_OFFSET, "".concat(offset, "px"));
142
148
  element.style.setProperty(_dropTargetV.EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_GAP, "".concat(gap, "px"));
143
- }
144
- if ((0, _platformFeatureFlags.fg)('platform_editor_drag_and_drop_target_v2')) {
149
+ element.style.setProperty('display', 'block');
145
150
  _reactDom.default.render( /*#__PURE__*/(0, _react.createElement)(_dropTargetV.DropTargetV2, _objectSpread(_objectSpread({}, props), {}, {
146
151
  getPos: getPos,
147
152
  anchorRectCache: anchorRectCache
@@ -221,16 +226,14 @@ var dropTargetDecorations = exports.dropTargetDecorations = function dropTargetD
221
226
 
222
227
  // only table and layout need to render full height drop target
223
228
  var isInSupportedContainer = (0, _platformFeatureFlags.fg)('platform_editor_drag_and_drop_target_v2') && ['tableCell', 'tableHeader', 'layoutColumn'].includes((parent === null || parent === void 0 ? void 0 : parent.type.name) || '');
224
-
225
- // container with only an empty paragrah
226
- var shouldShowFullHeight = isInSupportedContainer && (parent === null || parent === void 0 ? void 0 : parent.lastChild) === node && (parent === null || parent === void 0 ? void 0 : parent.childCount) === 1 && (0, _utils.isEmptyParagraph)(node) && (0, _platformFeatureFlags.fg)('platform_editor_drag_and_drop_target_v2');
229
+ var shouldShowFullHeight = isInSupportedContainer && (parent === null || parent === void 0 ? void 0 : parent.lastChild) === node && (0, _utils.isEmptyParagraph)(node);
227
230
  decs.push(createDropTargetDecoration(pos, {
228
231
  api: api,
229
232
  prevNode: previousNode,
230
233
  nextNode: node,
231
234
  parentNode: parent || undefined,
232
235
  formatMessage: formatMessage,
233
- dropTargetStyle: shouldShowFullHeight ? 'fullHeight' : 'default'
236
+ dropTargetStyle: shouldShowFullHeight ? 'remainingHeight' : 'default'
234
237
  }, -1, anchorRectCache));
235
238
  if (endPos !== undefined) {
236
239
  decs.push(createDropTargetDecoration(endPos, {
@@ -238,7 +241,7 @@ var dropTargetDecorations = exports.dropTargetDecorations = function dropTargetD
238
241
  prevNode: (0, _platformFeatureFlags.fg)('platform_editor_drag_and_drop_target_v2') ? node : undefined,
239
242
  parentNode: parent || undefined,
240
243
  formatMessage: formatMessage,
241
- dropTargetStyle: isInSupportedContainer ? 'fullHeight' : 'default'
244
+ dropTargetStyle: 'remainingHeight'
242
245
  }, -1, anchorRectCache));
243
246
  }
244
247
  if ((0, _platformFeatureFlags.fg)('platform_editor_drag_and_drop_target_v2')) {
@@ -293,6 +296,16 @@ var shouldIgnoreNode = function shouldIgnoreNode(node) {
293
296
  }
294
297
  return IGNORE_NODES.includes(node.type.name);
295
298
  };
299
+ var shouldDescendIntoNode = exports.shouldDescendIntoNode = function shouldDescendIntoNode(node) {
300
+ // Optimisation to avoid drawing node decorations for empty table cells
301
+ if (['tableCell', 'tableHeader'].includes(node.type.name) && !(0, _experiments.editorExperiment)('table-nested-dnd', true) && (0, _platformFeatureFlags.fg)('platform_editor_element_dnd_nested_fix_patch_3')) {
302
+ var _node$firstChild;
303
+ if (node.childCount === 1 && ((_node$firstChild = node.firstChild) === null || _node$firstChild === void 0 ? void 0 : _node$firstChild.type.name) === 'paragraph') {
304
+ return false;
305
+ }
306
+ }
307
+ return !IGNORE_NODE_DESCENDANTS.includes(node.type.name);
308
+ };
296
309
  var nodeDecorations = exports.nodeDecorations = function nodeDecorations(newState, from, to) {
297
310
  var decs = [];
298
311
  var docFrom = from === undefined || from < 0 ? 0 : from;
@@ -301,7 +314,7 @@ var nodeDecorations = exports.nodeDecorations = function nodeDecorations(newStat
301
314
  var _Decoration$node2;
302
315
  var depth = 0;
303
316
  var anchorName;
304
- var shouldDescend = !IGNORE_NODE_DESCENDANTS.includes(node.type.name);
317
+ var shouldDescend = shouldDescendIntoNode(node);
305
318
  var handleId = ObjHash.getForNode(node);
306
319
  anchorName = "--node-anchor-".concat(node.type.name, "-").concat(handleId);
307
320
  if ((0, _experiments.editorExperiment)('nested-dnd', true)) {
@@ -162,6 +162,10 @@ var nodeMargins = exports.nodeMargins = {
162
162
  top: 8,
163
163
  bottom: 0
164
164
  },
165
+ blockquote: {
166
+ top: 12,
167
+ bottom: 0
168
+ },
165
169
  default: {
166
170
  top: 0,
167
171
  bottom: 0
@@ -65,12 +65,6 @@ var nestedDropZoneStyle = (0, _react2.css)({
65
65
  width: 'unset'
66
66
  });
67
67
  var enableDropZone = ['paragraph', 'mediaSingle', 'heading', 'codeBlock', 'decisionList', 'bulletList', 'orderedList', 'taskList', 'extension', 'blockCard'];
68
- var fullHeightStyle = (0, _react2.css)({
69
- top: '4px',
70
- bottom: '4px',
71
- height: 'unset',
72
- zIndex: 10
73
- });
74
68
 
75
69
  // This z index is used in container like layout
76
70
  var fullHeightStyleAdjustZIndexStyle = (0, _react2.css)({
@@ -98,6 +92,7 @@ var HoverZone = function HoverZone(_ref) {
98
92
  });
99
93
  }
100
94
  }, [onDragEnter, onDragLeave, onDrop]);
95
+ var isRemainingheight = dropTargetStyle === 'remainingHeight';
101
96
  var hoverZoneUpperStyle = (0, _react.useMemo)(function () {
102
97
  var anchorName = node ? (0, _decorations.getNodeAnchor)(node) : '';
103
98
  var heightStyleOffset = "var(--editor-block-controls-drop-indicator-gap, 0)/2";
@@ -113,12 +108,47 @@ var HoverZone = function HoverZone(_ref) {
113
108
  maxWidth: "".concat(editorWidth || 0, "px")
114
109
  });
115
110
  }, [anchorRectCache, editorWidth, node, position]);
116
- var isFullHeight = (0, _react.useMemo)(function () {
117
- return dropTargetStyle === 'fullHeight';
118
- }, [dropTargetStyle]);
119
- var isFullHeightInLayout = (0, _react.useMemo)(function () {
120
- return isFullHeight && (parent === null || parent === void 0 ? void 0 : parent.type.name) === 'layoutColumn';
121
- }, [isFullHeight, parent === null || parent === void 0 ? void 0 : parent.type.name]);
111
+
112
+ /**
113
+ * 1. Above the last empty line
114
+ * 2. Below the last element
115
+ *
116
+ * Both cases will take the remaining height of the the container
117
+ */
118
+ var heightStyle = (0, _react.useMemo)(function () {
119
+ // only apply upper drop zone
120
+ if (isRemainingheight && position === 'upper') {
121
+ // previous node
122
+ var anchorName = node ? (0, _decorations.getNodeAnchor)(node) : '';
123
+ var top = 'unset';
124
+ if (anchorName) {
125
+ var enabledDropZone = enableDropZone.includes((node === null || node === void 0 ? void 0 : node.type.name) || '');
126
+ if ((0, _anchorUtils.isAnchorSupported)()) {
127
+ top = enabledDropZone ? "calc(anchor(".concat(anchorName, " 50%))") : "calc(anchor(".concat(anchorName, " bottom) - 4px)");
128
+ } else if (anchorRectCache) {
129
+ var preNodeTopPos = anchorRectCache.getTop(anchorName) || 0;
130
+ var prevNodeHeight = anchorRectCache.getHeight(anchorName) || 0;
131
+ top = enabledDropZone ? "calc(".concat(preNodeTopPos, "px + ").concat(prevNodeHeight / 2, "px)") : "calc(".concat(preNodeTopPos, "px + ").concat(prevNodeHeight, "px - 4px)");
132
+ } else {
133
+ // Should not happen
134
+ return null;
135
+ }
136
+ } else {
137
+ // first empty paragraph
138
+ top = '4px';
139
+ }
140
+ return (0, _react2.css)({
141
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values
142
+ top: top,
143
+ bottom: '4px',
144
+ height: 'unset',
145
+ zIndex: 10,
146
+ transform: 'none'
147
+ });
148
+ }
149
+ return null;
150
+ }, [anchorRectCache, isRemainingheight, node, position]);
151
+ var isFullHeightInLayout = isRemainingheight && (parent === null || parent === void 0 ? void 0 : parent.type.name) === 'layoutColumn';
122
152
  return (0, _react2.jsx)("div", {
123
153
  ref: ref
124
154
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
@@ -129,7 +159,9 @@ var HoverZone = function HoverZone(_ref) {
129
159
  ,
130
160
  css: [dropZoneStyles, isNestedDropTarget && nestedDropZoneStyle,
131
161
  // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
132
- hoverZoneUpperStyle, isFullHeight && fullHeightStyle, isFullHeightInLayout && fullHeightStyleAdjustZIndexStyle]
162
+ hoverZoneUpperStyle,
163
+ // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
164
+ heightStyle, isFullHeightInLayout && fullHeightStyleAdjustZIndexStyle]
133
165
  });
134
166
  };
135
167
  var DropTargetV2 = exports.DropTargetV2 = function DropTargetV2(props) {
@@ -141,7 +173,8 @@ var DropTargetV2 = exports.DropTargetV2 = function DropTargetV2(props) {
141
173
  parentNode = props.parentNode,
142
174
  formatMessage = props.formatMessage,
143
175
  anchorRectCache = props.anchorRectCache,
144
- dropTargetStyle = props.dropTargetStyle;
176
+ _props$dropTargetStyl = props.dropTargetStyle,
177
+ dropTargetStyle = _props$dropTargetStyl === void 0 ? 'default' : _props$dropTargetStyl;
145
178
  var _useState = (0, _react.useState)(false),
146
179
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
147
180
  isDraggedOver = _useState2[0],
@@ -163,11 +196,10 @@ var DropTargetV2 = exports.DropTargetV2 = function DropTargetV2(props) {
163
196
  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 || (_api$blockControls2 = _api$blockControls2.commands) === null || _api$blockControls2 === void 0 ? void 0 : _api$blockControls2.moveNode(start, pos, undefined, formatMessage));
164
197
  }
165
198
  };
166
- var isFullHeight = dropTargetStyle === 'fullHeight';
167
199
  var dynamicStyle = (_dynamicStyle = {
168
200
  width: isNestedDropTarget ? 'unset' : '100%'
169
201
  }, (0, _defineProperty2.default)(_dynamicStyle, EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_WIDTH, isNestedDropTarget ? '100%' : "".concat((widthState === null || widthState === void 0 ? void 0 : widthState.lineLength) || DEFAULT_DROP_INDICATOR_WIDTH, "px")), (0, _defineProperty2.default)(_dynamicStyle, EDITOR_BLOCK_CONTROLS_DROP_TARGET_LEFT_MARGIN, isNestedDropTarget ? (0, _consts.getNestedNodeLeftPaddingMargin)(parentNode === null || parentNode === void 0 ? void 0 : parentNode.type.name) : '0'), (0, _defineProperty2.default)(_dynamicStyle, EDITOR_BLOCK_CONTROLS_DROP_TARGET_ZINDEX, (0, _experiments.editorExperiment)('nested-dnd', true) ? _constants.layers.navigation() : _constants.layers.card()), _dynamicStyle);
170
- return (0, _react2.jsx)(_react.Fragment, null, !isFullHeight && (0, _react2.jsx)(HoverZone, {
202
+ return (0, _react2.jsx)(_react.Fragment, null, (0, _react2.jsx)(HoverZone, {
171
203
  onDragEnter: function onDragEnter() {
172
204
  return setIsDraggedOver(true);
173
205
  },
@@ -179,7 +211,8 @@ var DropTargetV2 = exports.DropTargetV2 = function DropTargetV2(props) {
179
211
  editorWidth: widthState === null || widthState === void 0 ? void 0 : widthState.lineLength,
180
212
  anchorRectCache: anchorRectCache,
181
213
  position: "upper",
182
- isNestedDropTarget: isNestedDropTarget
214
+ isNestedDropTarget: isNestedDropTarget,
215
+ dropTargetStyle: dropTargetStyle
183
216
  }), (0, _react2.jsx)("div", {
184
217
  // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
185
218
  css: [styleDropTarget, isNestedDropTarget && nestedDropIndicatorStyle]
@@ -194,7 +227,7 @@ var DropTargetV2 = exports.DropTargetV2 = function DropTargetV2(props) {
194
227
  "data-testid": "block-ctrl-drop-indicator"
195
228
  }, (0, _react2.jsx)(_box.DropIndicator, {
196
229
  edge: "bottom"
197
- }))), (0, _react2.jsx)(HoverZone, {
230
+ }))), dropTargetStyle !== 'remainingHeight' && (0, _react2.jsx)(HoverZone, {
198
231
  onDragEnter: function onDragEnter() {
199
232
  return setIsDraggedOver(true);
200
233
  },
@@ -207,8 +240,7 @@ var DropTargetV2 = exports.DropTargetV2 = function DropTargetV2(props) {
207
240
  editorWidth: widthState === null || widthState === void 0 ? void 0 : widthState.lineLength,
208
241
  anchorRectCache: anchorRectCache,
209
242
  position: "lower",
210
- isNestedDropTarget: isNestedDropTarget,
211
- dropTargetStyle: dropTargetStyle
243
+ isNestedDropTarget: isNestedDropTarget
212
244
  }), (0, _inlineDropTarget.shouldAllowInlineDropTarget)(isNestedDropTarget, nextNode) && (0, _react2.jsx)(_react.Fragment, null, (0, _react2.jsx)(_inlineDropTarget2.InlineDropTarget, (0, _extends2.default)({}, props, {
213
245
  position: "left"
214
246
  })), (0, _react2.jsx)(_inlineDropTarget2.InlineDropTarget, (0, _extends2.default)({}, props, {
@@ -1,8 +1,8 @@
1
1
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
+ import { MAX_LAYOUT_COLUMN_SUPPORTED } from '../consts';
2
3
  import { DEFAULT_COLUMN_DISTRIBUTIONS } from '../ui/consts';
3
4
  const createNewLayout = (schema, layoutContents) => {
4
- // TODO update with constant
5
- if (layoutContents.length === 0 || layoutContents.length > 5) {
5
+ if (layoutContents.length === 0 || layoutContents.length > MAX_LAYOUT_COLUMN_SUPPORTED) {
6
6
  return null;
7
7
  }
8
8
  const width = DEFAULT_COLUMN_DISTRIBUTIONS[layoutContents.length];
@@ -14,11 +14,12 @@ const createNewLayout = (schema, layoutContents) => {
14
14
  layoutColumn
15
15
  } = schema.nodes || {};
16
16
  try {
17
- const layoutSectionNode = layoutSection.createChecked(undefined, Fragment.fromArray(layoutContents.map(layoutContent => {
17
+ const layoutContent = Fragment.fromArray(layoutContents.map(layoutContent => {
18
18
  return layoutColumn.createChecked({
19
19
  width
20
20
  }, layoutContent);
21
- })));
21
+ }));
22
+ const layoutSectionNode = layoutSection.createChecked(undefined, layoutContent);
22
23
  return layoutSectionNode;
23
24
  } catch (error) {
24
25
  // TODO analytics
@@ -28,11 +29,18 @@ const createNewLayout = (schema, layoutContents) => {
28
29
  export const moveToLayout = api => (from, to, position) => ({
29
30
  tr
30
31
  }) => {
32
+ // unable to drag a node to itself.
33
+ if (from === to) {
34
+ return tr;
35
+ }
31
36
  const {
32
37
  layoutSection,
33
38
  layoutColumn,
34
39
  doc
35
40
  } = tr.doc.type.schema.nodes || {};
41
+ const {
42
+ breakout
43
+ } = tr.doc.type.schema.marks || {};
36
44
 
37
45
  // layout plugin does not exist
38
46
  if (!layoutSection || !layoutColumn) {
@@ -46,24 +54,44 @@ export const moveToLayout = api => (from, to, position) => ({
46
54
  }
47
55
  const $from = tr.doc.resolve(from);
48
56
 
49
- // invalid from position
50
- if (!$from.nodeAfter) {
57
+ // invalid from position or dragging a layout
58
+ if (!$from.nodeAfter || $from.nodeAfter.type === layoutSection) {
51
59
  return tr;
52
60
  }
53
61
  const toNode = $to.nodeAfter;
54
62
  const fromNode = $from.nodeAfter;
55
- const fromNodeEndPos = from + fromNode.nodeSize;
56
- const toNodeEndPos = to + toNode.nodeSize;
57
- if ($to.nodeAfter.type !== layoutSection) {
58
- const layoutContents = position === 'left' ? [fromNode, toNode] : [toNode, fromNode];
63
+
64
+ // remove breakout from node;
65
+ if (breakout && $from.nodeAfter && $from.nodeAfter.marks.some(m => m.type === breakout)) {
66
+ tr = tr.removeNodeMark(from, breakout);
67
+ }
68
+ if ($to.nodeAfter.type === layoutSection) {
69
+ const existingLayoutNode = $to.nodeAfter;
70
+ if (existingLayoutNode.childCount < MAX_LAYOUT_COLUMN_SUPPORTED) {
71
+ const toPos = position === 'left' ? to + 1 : to + existingLayoutNode.nodeSize - 1;
72
+ tr = tr.insert(toPos,
73
+ // resolve again the source node after node updated (remove breakout marks)
74
+ layoutColumn.create(null, tr.doc.resolve(from).nodeAfter));
75
+ const mappedFrom = tr.mapping.map(from);
76
+ const mappedFromEnd = mappedFrom + fromNode.nodeSize;
77
+ tr = tr.delete(mappedFrom, mappedFromEnd);
78
+ return tr;
79
+ }
80
+ return tr;
81
+ } else {
82
+ // resolve again the source node after node updated (remove breakout marks)
83
+ const newFromNode = tr.doc.resolve(from).nodeAfter;
84
+ if (!newFromNode) {
85
+ return tr;
86
+ }
87
+ const layoutContents = position === 'left' ? [newFromNode, toNode] : [toNode, newFromNode];
59
88
  const newLayout = createNewLayout(tr.doc.type.schema, layoutContents);
60
89
  if (newLayout) {
61
- tr.delete(from, fromNodeEndPos);
90
+ tr = tr.delete(from, from + fromNode.nodeSize);
62
91
  const mappedTo = tr.mapping.map(to);
63
- tr.delete(mappedTo, toNodeEndPos);
64
- tr.insert(mappedTo, newLayout); // insert the content at the new position
92
+ tr = tr.delete(mappedTo, mappedTo + toNode.nodeSize);
93
+ tr = tr.insert(mappedTo, newLayout); // insert the content at the new position
65
94
  }
66
95
  return tr;
67
96
  }
68
- return tr;
69
97
  };
@@ -35,6 +35,12 @@ const getNodeMargins = node => {
35
35
  }
36
36
  return nodeMargins[nodeTypeName] || nodeMargins['default'];
37
37
  };
38
+ const shouldCollapseMargin = (prevNode, nextNode) => {
39
+ if (((prevNode === null || prevNode === void 0 ? void 0 : prevNode.type.name) === 'mediaSingle' || (nextNode === null || nextNode === void 0 ? void 0 : nextNode.type.name) === 'mediaSingle') && (prevNode === null || prevNode === void 0 ? void 0 : prevNode.type.name) !== (nextNode === null || nextNode === void 0 ? void 0 : nextNode.type.name)) {
40
+ return false;
41
+ }
42
+ return true;
43
+ };
38
44
  const getGapAndOffset = (prevNode, nextNode, parentNode) => {
39
45
  if (!prevNode && nextNode) {
40
46
  // first node
@@ -50,7 +56,7 @@ const getGapAndOffset = (prevNode, nextNode, parentNode) => {
50
56
  }
51
57
  const top = getNodeMargins(nextNode).top || 4;
52
58
  const bottom = getNodeMargins(prevNode).bottom || 4;
53
- const gap = Math.max(top, bottom);
59
+ const gap = shouldCollapseMargin(prevNode, nextNode) ? Math.max(top, bottom) : top + bottom;
54
60
  let offset = top - gap / 2;
55
61
  if ((prevNode === null || prevNode === void 0 ? void 0 : prevNode.type.name) === 'mediaSingle' && (nextNode === null || nextNode === void 0 ? void 0 : nextNode.type.name) === 'mediaSingle') {
56
62
  offset = -offset;
@@ -122,8 +128,7 @@ export const createDropTargetDecoration = (pos, props, side, anchorRectCache) =>
122
128
  } = getGapAndOffset(props.prevNode, props.nextNode, props.parentNode);
123
129
  element.style.setProperty(EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_OFFSET, `${offset}px`);
124
130
  element.style.setProperty(EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_GAP, `${gap}px`);
125
- }
126
- if (fg('platform_editor_drag_and_drop_target_v2')) {
131
+ element.style.setProperty('display', 'block');
127
132
  ReactDOM.render( /*#__PURE__*/createElement(DropTargetV2, {
128
133
  ...props,
129
134
  getPos,
@@ -205,16 +210,14 @@ export const dropTargetDecorations = (newState, api, formatMessage, activeNode,
205
210
 
206
211
  // only table and layout need to render full height drop target
207
212
  const isInSupportedContainer = fg('platform_editor_drag_and_drop_target_v2') && ['tableCell', 'tableHeader', 'layoutColumn'].includes((parent === null || parent === void 0 ? void 0 : parent.type.name) || '');
208
-
209
- // container with only an empty paragrah
210
- const shouldShowFullHeight = isInSupportedContainer && (parent === null || parent === void 0 ? void 0 : parent.lastChild) === node && (parent === null || parent === void 0 ? void 0 : parent.childCount) === 1 && isEmptyParagraph(node) && fg('platform_editor_drag_and_drop_target_v2');
213
+ const shouldShowFullHeight = isInSupportedContainer && (parent === null || parent === void 0 ? void 0 : parent.lastChild) === node && isEmptyParagraph(node);
211
214
  decs.push(createDropTargetDecoration(pos, {
212
215
  api,
213
216
  prevNode: previousNode,
214
217
  nextNode: node,
215
218
  parentNode: parent || undefined,
216
219
  formatMessage,
217
- dropTargetStyle: shouldShowFullHeight ? 'fullHeight' : 'default'
220
+ dropTargetStyle: shouldShowFullHeight ? 'remainingHeight' : 'default'
218
221
  }, -1, anchorRectCache));
219
222
  if (endPos !== undefined) {
220
223
  decs.push(createDropTargetDecoration(endPos, {
@@ -222,7 +225,7 @@ export const dropTargetDecorations = (newState, api, formatMessage, activeNode,
222
225
  prevNode: fg('platform_editor_drag_and_drop_target_v2') ? node : undefined,
223
226
  parentNode: parent || undefined,
224
227
  formatMessage,
225
- dropTargetStyle: isInSupportedContainer ? 'fullHeight' : 'default'
228
+ dropTargetStyle: 'remainingHeight'
226
229
  }, -1, anchorRectCache));
227
230
  }
228
231
  if (fg('platform_editor_drag_and_drop_target_v2')) {
@@ -271,6 +274,16 @@ const shouldIgnoreNode = node => {
271
274
  }
272
275
  return IGNORE_NODES.includes(node.type.name);
273
276
  };
277
+ export const shouldDescendIntoNode = node => {
278
+ // Optimisation to avoid drawing node decorations for empty table cells
279
+ if (['tableCell', 'tableHeader'].includes(node.type.name) && !editorExperiment('table-nested-dnd', true) && fg('platform_editor_element_dnd_nested_fix_patch_3')) {
280
+ var _node$firstChild;
281
+ if (node.childCount === 1 && ((_node$firstChild = node.firstChild) === null || _node$firstChild === void 0 ? void 0 : _node$firstChild.type.name) === 'paragraph') {
282
+ return false;
283
+ }
284
+ }
285
+ return !IGNORE_NODE_DESCENDANTS.includes(node.type.name);
286
+ };
274
287
  export const nodeDecorations = (newState, from, to) => {
275
288
  const decs = [];
276
289
  const docFrom = from === undefined || from < 0 ? 0 : from;
@@ -278,7 +291,7 @@ export const nodeDecorations = (newState, from, to) => {
278
291
  newState.doc.nodesBetween(docFrom, docTo, (node, pos, _parent, index) => {
279
292
  let depth = 0;
280
293
  let anchorName;
281
- const shouldDescend = !IGNORE_NODE_DESCENDANTS.includes(node.type.name);
294
+ const shouldDescend = shouldDescendIntoNode(node);
282
295
  const handleId = ObjHash.getForNode(node);
283
296
  anchorName = `--node-anchor-${node.type.name}-${handleId}`;
284
297
  if (editorExperiment('nested-dnd', true)) {
@@ -171,6 +171,10 @@ export const nodeMargins = {
171
171
  top: 8,
172
172
  bottom: 0
173
173
  },
174
+ blockquote: {
175
+ top: 12,
176
+ bottom: 0
177
+ },
174
178
  default: {
175
179
  top: 0,
176
180
  bottom: 0
@@ -55,12 +55,6 @@ const nestedDropZoneStyle = css({
55
55
  width: 'unset'
56
56
  });
57
57
  const enableDropZone = ['paragraph', 'mediaSingle', 'heading', 'codeBlock', 'decisionList', 'bulletList', 'orderedList', 'taskList', 'extension', 'blockCard'];
58
- const fullHeightStyle = css({
59
- top: '4px',
60
- bottom: '4px',
61
- height: 'unset',
62
- zIndex: 10
63
- });
64
58
 
65
59
  // This z index is used in container like layout
66
60
  const fullHeightStyleAdjustZIndexStyle = css({
@@ -89,6 +83,7 @@ const HoverZone = ({
89
83
  });
90
84
  }
91
85
  }, [onDragEnter, onDragLeave, onDrop]);
86
+ const isRemainingheight = dropTargetStyle === 'remainingHeight';
92
87
  const hoverZoneUpperStyle = useMemo(() => {
93
88
  const anchorName = node ? getNodeAnchor(node) : '';
94
89
  const heightStyleOffset = `var(--editor-block-controls-drop-indicator-gap, 0)/2`;
@@ -104,12 +99,47 @@ const HoverZone = ({
104
99
  maxWidth: `${editorWidth || 0}px`
105
100
  });
106
101
  }, [anchorRectCache, editorWidth, node, position]);
107
- const isFullHeight = useMemo(() => {
108
- return dropTargetStyle === 'fullHeight';
109
- }, [dropTargetStyle]);
110
- const isFullHeightInLayout = useMemo(() => {
111
- return isFullHeight && (parent === null || parent === void 0 ? void 0 : parent.type.name) === 'layoutColumn';
112
- }, [isFullHeight, parent === null || parent === void 0 ? void 0 : parent.type.name]);
102
+
103
+ /**
104
+ * 1. Above the last empty line
105
+ * 2. Below the last element
106
+ *
107
+ * Both cases will take the remaining height of the the container
108
+ */
109
+ const heightStyle = useMemo(() => {
110
+ // only apply upper drop zone
111
+ if (isRemainingheight && position === 'upper') {
112
+ // previous node
113
+ const anchorName = node ? getNodeAnchor(node) : '';
114
+ let top = 'unset';
115
+ if (anchorName) {
116
+ const enabledDropZone = enableDropZone.includes((node === null || node === void 0 ? void 0 : node.type.name) || '');
117
+ if (isAnchorSupported()) {
118
+ top = enabledDropZone ? `calc(anchor(${anchorName} 50%))` : `calc(anchor(${anchorName} bottom) - 4px)`;
119
+ } else if (anchorRectCache) {
120
+ const preNodeTopPos = anchorRectCache.getTop(anchorName) || 0;
121
+ const prevNodeHeight = anchorRectCache.getHeight(anchorName) || 0;
122
+ top = enabledDropZone ? `calc(${preNodeTopPos}px + ${prevNodeHeight / 2}px)` : `calc(${preNodeTopPos}px + ${prevNodeHeight}px - 4px)`;
123
+ } else {
124
+ // Should not happen
125
+ return null;
126
+ }
127
+ } else {
128
+ // first empty paragraph
129
+ top = '4px';
130
+ }
131
+ return css({
132
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values
133
+ top: top,
134
+ bottom: '4px',
135
+ height: 'unset',
136
+ zIndex: 10,
137
+ transform: 'none'
138
+ });
139
+ }
140
+ return null;
141
+ }, [anchorRectCache, isRemainingheight, node, position]);
142
+ const isFullHeightInLayout = isRemainingheight && (parent === null || parent === void 0 ? void 0 : parent.type.name) === 'layoutColumn';
113
143
  return jsx("div", {
114
144
  ref: ref
115
145
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
@@ -120,7 +150,9 @@ const HoverZone = ({
120
150
  ,
121
151
  css: [dropZoneStyles, isNestedDropTarget && nestedDropZoneStyle,
122
152
  // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
123
- hoverZoneUpperStyle, isFullHeight && fullHeightStyle, isFullHeightInLayout && fullHeightStyleAdjustZIndexStyle]
153
+ hoverZoneUpperStyle,
154
+ // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
155
+ heightStyle, isFullHeightInLayout && fullHeightStyleAdjustZIndexStyle]
124
156
  });
125
157
  };
126
158
  export const DropTargetV2 = props => {
@@ -132,7 +164,7 @@ export const DropTargetV2 = props => {
132
164
  parentNode,
133
165
  formatMessage,
134
166
  anchorRectCache,
135
- dropTargetStyle
167
+ dropTargetStyle = 'default'
136
168
  } = props;
137
169
  const [isDraggedOver, setIsDraggedOver] = useState(false);
138
170
  const {
@@ -156,14 +188,13 @@ export const DropTargetV2 = props => {
156
188
  api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(api === null || api === void 0 ? void 0 : (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 ? void 0 : (_api$blockControls2$c = _api$blockControls2.commands) === null || _api$blockControls2$c === void 0 ? void 0 : _api$blockControls2$c.moveNode(start, pos, undefined, formatMessage));
157
189
  }
158
190
  };
159
- const isFullHeight = dropTargetStyle === 'fullHeight';
160
191
  const dynamicStyle = {
161
192
  width: isNestedDropTarget ? 'unset' : '100%',
162
193
  [EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_WIDTH]: isNestedDropTarget ? '100%' : `${(widthState === null || widthState === void 0 ? void 0 : widthState.lineLength) || DEFAULT_DROP_INDICATOR_WIDTH}px`,
163
194
  [EDITOR_BLOCK_CONTROLS_DROP_TARGET_LEFT_MARGIN]: isNestedDropTarget ? getNestedNodeLeftPaddingMargin(parentNode === null || parentNode === void 0 ? void 0 : parentNode.type.name) : '0',
164
195
  [EDITOR_BLOCK_CONTROLS_DROP_TARGET_ZINDEX]: editorExperiment('nested-dnd', true) ? layers.navigation() : layers.card()
165
196
  };
166
- return jsx(Fragment, null, !isFullHeight && jsx(HoverZone, {
197
+ return jsx(Fragment, null, jsx(HoverZone, {
167
198
  onDragEnter: () => setIsDraggedOver(true),
168
199
  onDragLeave: () => setIsDraggedOver(false),
169
200
  onDrop: onDrop,
@@ -171,7 +202,8 @@ export const DropTargetV2 = props => {
171
202
  editorWidth: widthState === null || widthState === void 0 ? void 0 : widthState.lineLength,
172
203
  anchorRectCache: anchorRectCache,
173
204
  position: "upper",
174
- isNestedDropTarget: isNestedDropTarget
205
+ isNestedDropTarget: isNestedDropTarget,
206
+ dropTargetStyle: dropTargetStyle
175
207
  }), jsx("div", {
176
208
  // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
177
209
  css: [styleDropTarget, isNestedDropTarget && nestedDropIndicatorStyle]
@@ -186,7 +218,7 @@ export const DropTargetV2 = props => {
186
218
  "data-testid": "block-ctrl-drop-indicator"
187
219
  }, jsx(DropIndicator, {
188
220
  edge: "bottom"
189
- }))), jsx(HoverZone, {
221
+ }))), dropTargetStyle !== 'remainingHeight' && jsx(HoverZone, {
190
222
  onDragEnter: () => setIsDraggedOver(true),
191
223
  onDragLeave: () => setIsDraggedOver(false),
192
224
  onDrop: onDrop,
@@ -195,8 +227,7 @@ export const DropTargetV2 = props => {
195
227
  editorWidth: widthState === null || widthState === void 0 ? void 0 : widthState.lineLength,
196
228
  anchorRectCache: anchorRectCache,
197
229
  position: "lower",
198
- isNestedDropTarget: isNestedDropTarget,
199
- dropTargetStyle: dropTargetStyle
230
+ isNestedDropTarget: isNestedDropTarget
200
231
  }), shouldAllowInlineDropTarget(isNestedDropTarget, nextNode) && jsx(Fragment, null, jsx(InlineDropTarget, _extends({}, props, {
201
232
  position: "left"
202
233
  })), jsx(InlineDropTarget, _extends({}, props, {
@@ -1,8 +1,8 @@
1
1
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
+ import { MAX_LAYOUT_COLUMN_SUPPORTED } from '../consts';
2
3
  import { DEFAULT_COLUMN_DISTRIBUTIONS } from '../ui/consts';
3
4
  var createNewLayout = function createNewLayout(schema, layoutContents) {
4
- // TODO update with constant
5
- if (layoutContents.length === 0 || layoutContents.length > 5) {
5
+ if (layoutContents.length === 0 || layoutContents.length > MAX_LAYOUT_COLUMN_SUPPORTED) {
6
6
  return null;
7
7
  }
8
8
  var width = DEFAULT_COLUMN_DISTRIBUTIONS[layoutContents.length];
@@ -13,11 +13,12 @@ var createNewLayout = function createNewLayout(schema, layoutContents) {
13
13
  layoutSection = _ref.layoutSection,
14
14
  layoutColumn = _ref.layoutColumn;
15
15
  try {
16
- var layoutSectionNode = layoutSection.createChecked(undefined, Fragment.fromArray(layoutContents.map(function (layoutContent) {
16
+ var layoutContent = Fragment.fromArray(layoutContents.map(function (layoutContent) {
17
17
  return layoutColumn.createChecked({
18
18
  width: width
19
19
  }, layoutContent);
20
- })));
20
+ }));
21
+ var layoutSectionNode = layoutSection.createChecked(undefined, layoutContent);
21
22
  return layoutSectionNode;
22
23
  } catch (error) {
23
24
  // TODO analytics
@@ -28,10 +29,16 @@ export var moveToLayout = function moveToLayout(api) {
28
29
  return function (from, to, position) {
29
30
  return function (_ref2) {
30
31
  var tr = _ref2.tr;
32
+ // unable to drag a node to itself.
33
+ if (from === to) {
34
+ return tr;
35
+ }
31
36
  var _ref3 = tr.doc.type.schema.nodes || {},
32
37
  layoutSection = _ref3.layoutSection,
33
38
  layoutColumn = _ref3.layoutColumn,
34
39
  doc = _ref3.doc;
40
+ var _ref4 = tr.doc.type.schema.marks || {},
41
+ breakout = _ref4.breakout;
35
42
 
36
43
  // layout plugin does not exist
37
44
  if (!layoutSection || !layoutColumn) {
@@ -45,26 +52,48 @@ export var moveToLayout = function moveToLayout(api) {
45
52
  }
46
53
  var $from = tr.doc.resolve(from);
47
54
 
48
- // invalid from position
49
- if (!$from.nodeAfter) {
55
+ // invalid from position or dragging a layout
56
+ if (!$from.nodeAfter || $from.nodeAfter.type === layoutSection) {
50
57
  return tr;
51
58
  }
52
59
  var toNode = $to.nodeAfter;
53
60
  var fromNode = $from.nodeAfter;
54
- var fromNodeEndPos = from + fromNode.nodeSize;
55
- var toNodeEndPos = to + toNode.nodeSize;
56
- if ($to.nodeAfter.type !== layoutSection) {
57
- var layoutContents = position === 'left' ? [fromNode, toNode] : [toNode, fromNode];
61
+
62
+ // remove breakout from node;
63
+ if (breakout && $from.nodeAfter && $from.nodeAfter.marks.some(function (m) {
64
+ return m.type === breakout;
65
+ })) {
66
+ tr = tr.removeNodeMark(from, breakout);
67
+ }
68
+ if ($to.nodeAfter.type === layoutSection) {
69
+ var existingLayoutNode = $to.nodeAfter;
70
+ if (existingLayoutNode.childCount < MAX_LAYOUT_COLUMN_SUPPORTED) {
71
+ var toPos = position === 'left' ? to + 1 : to + existingLayoutNode.nodeSize - 1;
72
+ tr = tr.insert(toPos,
73
+ // resolve again the source node after node updated (remove breakout marks)
74
+ layoutColumn.create(null, tr.doc.resolve(from).nodeAfter));
75
+ var mappedFrom = tr.mapping.map(from);
76
+ var mappedFromEnd = mappedFrom + fromNode.nodeSize;
77
+ tr = tr.delete(mappedFrom, mappedFromEnd);
78
+ return tr;
79
+ }
80
+ return tr;
81
+ } else {
82
+ // resolve again the source node after node updated (remove breakout marks)
83
+ var newFromNode = tr.doc.resolve(from).nodeAfter;
84
+ if (!newFromNode) {
85
+ return tr;
86
+ }
87
+ var layoutContents = position === 'left' ? [newFromNode, toNode] : [toNode, newFromNode];
58
88
  var newLayout = createNewLayout(tr.doc.type.schema, layoutContents);
59
89
  if (newLayout) {
60
- tr.delete(from, fromNodeEndPos);
90
+ tr = tr.delete(from, from + fromNode.nodeSize);
61
91
  var mappedTo = tr.mapping.map(to);
62
- tr.delete(mappedTo, toNodeEndPos);
63
- tr.insert(mappedTo, newLayout); // insert the content at the new position
92
+ tr = tr.delete(mappedTo, mappedTo + toNode.nodeSize);
93
+ tr = tr.insert(mappedTo, newLayout); // insert the content at the new position
64
94
  }
65
95
  return tr;
66
96
  }
67
- return tr;
68
97
  };
69
98
  };
70
99
  };
@@ -41,6 +41,12 @@ var getNodeMargins = function getNodeMargins(node) {
41
41
  }
42
42
  return nodeMargins[nodeTypeName] || nodeMargins['default'];
43
43
  };
44
+ var shouldCollapseMargin = function shouldCollapseMargin(prevNode, nextNode) {
45
+ if (((prevNode === null || prevNode === void 0 ? void 0 : prevNode.type.name) === 'mediaSingle' || (nextNode === null || nextNode === void 0 ? void 0 : nextNode.type.name) === 'mediaSingle') && (prevNode === null || prevNode === void 0 ? void 0 : prevNode.type.name) !== (nextNode === null || nextNode === void 0 ? void 0 : nextNode.type.name)) {
46
+ return false;
47
+ }
48
+ return true;
49
+ };
44
50
  var getGapAndOffset = function getGapAndOffset(prevNode, nextNode, parentNode) {
45
51
  if (!prevNode && nextNode) {
46
52
  // first node
@@ -56,7 +62,7 @@ var getGapAndOffset = function getGapAndOffset(prevNode, nextNode, parentNode) {
56
62
  }
57
63
  var top = getNodeMargins(nextNode).top || 4;
58
64
  var bottom = getNodeMargins(prevNode).bottom || 4;
59
- var gap = Math.max(top, bottom);
65
+ var gap = shouldCollapseMargin(prevNode, nextNode) ? Math.max(top, bottom) : top + bottom;
60
66
  var offset = top - gap / 2;
61
67
  if ((prevNode === null || prevNode === void 0 ? void 0 : prevNode.type.name) === 'mediaSingle' && (nextNode === null || nextNode === void 0 ? void 0 : nextNode.type.name) === 'mediaSingle') {
62
68
  offset = -offset;
@@ -133,8 +139,7 @@ export var createDropTargetDecoration = function createDropTargetDecoration(pos,
133
139
  offset = _getGapAndOffset.offset;
134
140
  element.style.setProperty(EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_OFFSET, "".concat(offset, "px"));
135
141
  element.style.setProperty(EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_GAP, "".concat(gap, "px"));
136
- }
137
- if (fg('platform_editor_drag_and_drop_target_v2')) {
142
+ element.style.setProperty('display', 'block');
138
143
  ReactDOM.render( /*#__PURE__*/createElement(DropTargetV2, _objectSpread(_objectSpread({}, props), {}, {
139
144
  getPos: getPos,
140
145
  anchorRectCache: anchorRectCache
@@ -214,16 +219,14 @@ export var dropTargetDecorations = function dropTargetDecorations(newState, api,
214
219
 
215
220
  // only table and layout need to render full height drop target
216
221
  var isInSupportedContainer = fg('platform_editor_drag_and_drop_target_v2') && ['tableCell', 'tableHeader', 'layoutColumn'].includes((parent === null || parent === void 0 ? void 0 : parent.type.name) || '');
217
-
218
- // container with only an empty paragrah
219
- var shouldShowFullHeight = isInSupportedContainer && (parent === null || parent === void 0 ? void 0 : parent.lastChild) === node && (parent === null || parent === void 0 ? void 0 : parent.childCount) === 1 && isEmptyParagraph(node) && fg('platform_editor_drag_and_drop_target_v2');
222
+ var shouldShowFullHeight = isInSupportedContainer && (parent === null || parent === void 0 ? void 0 : parent.lastChild) === node && isEmptyParagraph(node);
220
223
  decs.push(createDropTargetDecoration(pos, {
221
224
  api: api,
222
225
  prevNode: previousNode,
223
226
  nextNode: node,
224
227
  parentNode: parent || undefined,
225
228
  formatMessage: formatMessage,
226
- dropTargetStyle: shouldShowFullHeight ? 'fullHeight' : 'default'
229
+ dropTargetStyle: shouldShowFullHeight ? 'remainingHeight' : 'default'
227
230
  }, -1, anchorRectCache));
228
231
  if (endPos !== undefined) {
229
232
  decs.push(createDropTargetDecoration(endPos, {
@@ -231,7 +234,7 @@ export var dropTargetDecorations = function dropTargetDecorations(newState, api,
231
234
  prevNode: fg('platform_editor_drag_and_drop_target_v2') ? node : undefined,
232
235
  parentNode: parent || undefined,
233
236
  formatMessage: formatMessage,
234
- dropTargetStyle: isInSupportedContainer ? 'fullHeight' : 'default'
237
+ dropTargetStyle: 'remainingHeight'
235
238
  }, -1, anchorRectCache));
236
239
  }
237
240
  if (fg('platform_editor_drag_and_drop_target_v2')) {
@@ -286,6 +289,16 @@ var shouldIgnoreNode = function shouldIgnoreNode(node) {
286
289
  }
287
290
  return IGNORE_NODES.includes(node.type.name);
288
291
  };
292
+ export var shouldDescendIntoNode = function shouldDescendIntoNode(node) {
293
+ // Optimisation to avoid drawing node decorations for empty table cells
294
+ if (['tableCell', 'tableHeader'].includes(node.type.name) && !editorExperiment('table-nested-dnd', true) && fg('platform_editor_element_dnd_nested_fix_patch_3')) {
295
+ var _node$firstChild;
296
+ if (node.childCount === 1 && ((_node$firstChild = node.firstChild) === null || _node$firstChild === void 0 ? void 0 : _node$firstChild.type.name) === 'paragraph') {
297
+ return false;
298
+ }
299
+ }
300
+ return !IGNORE_NODE_DESCENDANTS.includes(node.type.name);
301
+ };
289
302
  export var nodeDecorations = function nodeDecorations(newState, from, to) {
290
303
  var decs = [];
291
304
  var docFrom = from === undefined || from < 0 ? 0 : from;
@@ -294,7 +307,7 @@ export var nodeDecorations = function nodeDecorations(newState, from, to) {
294
307
  var _Decoration$node2;
295
308
  var depth = 0;
296
309
  var anchorName;
297
- var shouldDescend = !IGNORE_NODE_DESCENDANTS.includes(node.type.name);
310
+ var shouldDescend = shouldDescendIntoNode(node);
298
311
  var handleId = ObjHash.getForNode(node);
299
312
  anchorName = "--node-anchor-".concat(node.type.name, "-").concat(handleId);
300
313
  if (editorExperiment('nested-dnd', true)) {
@@ -155,6 +155,10 @@ export var nodeMargins = {
155
155
  top: 8,
156
156
  bottom: 0
157
157
  },
158
+ blockquote: {
159
+ top: 12,
160
+ bottom: 0
161
+ },
158
162
  default: {
159
163
  top: 0,
160
164
  bottom: 0
@@ -57,12 +57,6 @@ var nestedDropZoneStyle = css({
57
57
  width: 'unset'
58
58
  });
59
59
  var enableDropZone = ['paragraph', 'mediaSingle', 'heading', 'codeBlock', 'decisionList', 'bulletList', 'orderedList', 'taskList', 'extension', 'blockCard'];
60
- var fullHeightStyle = css({
61
- top: '4px',
62
- bottom: '4px',
63
- height: 'unset',
64
- zIndex: 10
65
- });
66
60
 
67
61
  // This z index is used in container like layout
68
62
  var fullHeightStyleAdjustZIndexStyle = css({
@@ -90,6 +84,7 @@ var HoverZone = function HoverZone(_ref) {
90
84
  });
91
85
  }
92
86
  }, [onDragEnter, onDragLeave, onDrop]);
87
+ var isRemainingheight = dropTargetStyle === 'remainingHeight';
93
88
  var hoverZoneUpperStyle = useMemo(function () {
94
89
  var anchorName = node ? getNodeAnchor(node) : '';
95
90
  var heightStyleOffset = "var(--editor-block-controls-drop-indicator-gap, 0)/2";
@@ -105,12 +100,47 @@ var HoverZone = function HoverZone(_ref) {
105
100
  maxWidth: "".concat(editorWidth || 0, "px")
106
101
  });
107
102
  }, [anchorRectCache, editorWidth, node, position]);
108
- var isFullHeight = useMemo(function () {
109
- return dropTargetStyle === 'fullHeight';
110
- }, [dropTargetStyle]);
111
- var isFullHeightInLayout = useMemo(function () {
112
- return isFullHeight && (parent === null || parent === void 0 ? void 0 : parent.type.name) === 'layoutColumn';
113
- }, [isFullHeight, parent === null || parent === void 0 ? void 0 : parent.type.name]);
103
+
104
+ /**
105
+ * 1. Above the last empty line
106
+ * 2. Below the last element
107
+ *
108
+ * Both cases will take the remaining height of the the container
109
+ */
110
+ var heightStyle = useMemo(function () {
111
+ // only apply upper drop zone
112
+ if (isRemainingheight && position === 'upper') {
113
+ // previous node
114
+ var anchorName = node ? getNodeAnchor(node) : '';
115
+ var top = 'unset';
116
+ if (anchorName) {
117
+ var enabledDropZone = enableDropZone.includes((node === null || node === void 0 ? void 0 : node.type.name) || '');
118
+ if (isAnchorSupported()) {
119
+ top = enabledDropZone ? "calc(anchor(".concat(anchorName, " 50%))") : "calc(anchor(".concat(anchorName, " bottom) - 4px)");
120
+ } else if (anchorRectCache) {
121
+ var preNodeTopPos = anchorRectCache.getTop(anchorName) || 0;
122
+ var prevNodeHeight = anchorRectCache.getHeight(anchorName) || 0;
123
+ top = enabledDropZone ? "calc(".concat(preNodeTopPos, "px + ").concat(prevNodeHeight / 2, "px)") : "calc(".concat(preNodeTopPos, "px + ").concat(prevNodeHeight, "px - 4px)");
124
+ } else {
125
+ // Should not happen
126
+ return null;
127
+ }
128
+ } else {
129
+ // first empty paragraph
130
+ top = '4px';
131
+ }
132
+ return css({
133
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values
134
+ top: top,
135
+ bottom: '4px',
136
+ height: 'unset',
137
+ zIndex: 10,
138
+ transform: 'none'
139
+ });
140
+ }
141
+ return null;
142
+ }, [anchorRectCache, isRemainingheight, node, position]);
143
+ var isFullHeightInLayout = isRemainingheight && (parent === null || parent === void 0 ? void 0 : parent.type.name) === 'layoutColumn';
114
144
  return jsx("div", {
115
145
  ref: ref
116
146
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
@@ -121,7 +151,9 @@ var HoverZone = function HoverZone(_ref) {
121
151
  ,
122
152
  css: [dropZoneStyles, isNestedDropTarget && nestedDropZoneStyle,
123
153
  // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
124
- hoverZoneUpperStyle, isFullHeight && fullHeightStyle, isFullHeightInLayout && fullHeightStyleAdjustZIndexStyle]
154
+ hoverZoneUpperStyle,
155
+ // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
156
+ heightStyle, isFullHeightInLayout && fullHeightStyleAdjustZIndexStyle]
125
157
  });
126
158
  };
127
159
  export var DropTargetV2 = function DropTargetV2(props) {
@@ -133,7 +165,8 @@ export var DropTargetV2 = function DropTargetV2(props) {
133
165
  parentNode = props.parentNode,
134
166
  formatMessage = props.formatMessage,
135
167
  anchorRectCache = props.anchorRectCache,
136
- dropTargetStyle = props.dropTargetStyle;
168
+ _props$dropTargetStyl = props.dropTargetStyle,
169
+ dropTargetStyle = _props$dropTargetStyl === void 0 ? 'default' : _props$dropTargetStyl;
137
170
  var _useState = useState(false),
138
171
  _useState2 = _slicedToArray(_useState, 2),
139
172
  isDraggedOver = _useState2[0],
@@ -155,11 +188,10 @@ export var DropTargetV2 = function DropTargetV2(props) {
155
188
  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 || (_api$blockControls2 = _api$blockControls2.commands) === null || _api$blockControls2 === void 0 ? void 0 : _api$blockControls2.moveNode(start, pos, undefined, formatMessage));
156
189
  }
157
190
  };
158
- var isFullHeight = dropTargetStyle === 'fullHeight';
159
191
  var dynamicStyle = (_dynamicStyle = {
160
192
  width: isNestedDropTarget ? 'unset' : '100%'
161
193
  }, _defineProperty(_dynamicStyle, EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_WIDTH, isNestedDropTarget ? '100%' : "".concat((widthState === null || widthState === void 0 ? void 0 : widthState.lineLength) || DEFAULT_DROP_INDICATOR_WIDTH, "px")), _defineProperty(_dynamicStyle, EDITOR_BLOCK_CONTROLS_DROP_TARGET_LEFT_MARGIN, isNestedDropTarget ? getNestedNodeLeftPaddingMargin(parentNode === null || parentNode === void 0 ? void 0 : parentNode.type.name) : '0'), _defineProperty(_dynamicStyle, EDITOR_BLOCK_CONTROLS_DROP_TARGET_ZINDEX, editorExperiment('nested-dnd', true) ? layers.navigation() : layers.card()), _dynamicStyle);
162
- return jsx(Fragment, null, !isFullHeight && jsx(HoverZone, {
194
+ return jsx(Fragment, null, jsx(HoverZone, {
163
195
  onDragEnter: function onDragEnter() {
164
196
  return setIsDraggedOver(true);
165
197
  },
@@ -171,7 +203,8 @@ export var DropTargetV2 = function DropTargetV2(props) {
171
203
  editorWidth: widthState === null || widthState === void 0 ? void 0 : widthState.lineLength,
172
204
  anchorRectCache: anchorRectCache,
173
205
  position: "upper",
174
- isNestedDropTarget: isNestedDropTarget
206
+ isNestedDropTarget: isNestedDropTarget,
207
+ dropTargetStyle: dropTargetStyle
175
208
  }), jsx("div", {
176
209
  // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
177
210
  css: [styleDropTarget, isNestedDropTarget && nestedDropIndicatorStyle]
@@ -186,7 +219,7 @@ export var DropTargetV2 = function DropTargetV2(props) {
186
219
  "data-testid": "block-ctrl-drop-indicator"
187
220
  }, jsx(DropIndicator, {
188
221
  edge: "bottom"
189
- }))), jsx(HoverZone, {
222
+ }))), dropTargetStyle !== 'remainingHeight' && jsx(HoverZone, {
190
223
  onDragEnter: function onDragEnter() {
191
224
  return setIsDraggedOver(true);
192
225
  },
@@ -199,8 +232,7 @@ export var DropTargetV2 = function DropTargetV2(props) {
199
232
  editorWidth: widthState === null || widthState === void 0 ? void 0 : widthState.lineLength,
200
233
  anchorRectCache: anchorRectCache,
201
234
  position: "lower",
202
- isNestedDropTarget: isNestedDropTarget,
203
- dropTargetStyle: dropTargetStyle
235
+ isNestedDropTarget: isNestedDropTarget
204
236
  }), shouldAllowInlineDropTarget(isNestedDropTarget, nextNode) && jsx(Fragment, null, jsx(InlineDropTarget, _extends({}, props, {
205
237
  position: "left"
206
238
  })), jsx(InlineDropTarget, _extends({}, props, {
@@ -30,5 +30,6 @@ export declare const findNodeDecs: (decorations: DecorationSet, from?: number, t
30
30
  export declare const createDropTargetDecoration: (pos: number, props: Omit<DropTargetProps, 'getPos'>, side?: number, anchorRectCache?: AnchorRectCache) => Decoration;
31
31
  export declare const dropTargetDecorations: (newState: EditorState, api: ExtractInjectionAPI<BlockControlsPlugin>, formatMessage: IntlShape['formatMessage'], activeNode?: ActiveNode, anchorRectCache?: AnchorRectCache, from?: number, to?: number) => Decoration[];
32
32
  export declare const emptyParagraphNodeDecorations: () => Decoration;
33
+ export declare const shouldDescendIntoNode: (node: PMNode) => boolean;
33
34
  export declare const nodeDecorations: (newState: EditorState, from?: number, to?: number) => Decoration[];
34
35
  export declare const dragHandleDecoration: (api: ExtractInjectionAPI<BlockControlsPlugin>, formatMessage: IntlShape['formatMessage'], pos: number, anchorName: string, nodeType: string, handleOptions?: HandleOptions) => Decoration;
@@ -3,7 +3,7 @@ import { type IntlShape } from 'react-intl-next';
3
3
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
4
4
  import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
5
5
  import type { BlockControlsPlugin } from '../types';
6
- export type DropTargetStyle = 'default' | 'fullHeight';
6
+ export type DropTargetStyle = 'default' | 'remainingHeight';
7
7
  export type DropTargetProps = {
8
8
  api: ExtractInjectionAPI<BlockControlsPlugin> | undefined;
9
9
  getPos: () => number | undefined;
@@ -30,5 +30,6 @@ export declare const findNodeDecs: (decorations: DecorationSet, from?: number, t
30
30
  export declare const createDropTargetDecoration: (pos: number, props: Omit<DropTargetProps, 'getPos'>, side?: number, anchorRectCache?: AnchorRectCache) => Decoration;
31
31
  export declare const dropTargetDecorations: (newState: EditorState, api: ExtractInjectionAPI<BlockControlsPlugin>, formatMessage: IntlShape['formatMessage'], activeNode?: ActiveNode, anchorRectCache?: AnchorRectCache, from?: number, to?: number) => Decoration[];
32
32
  export declare const emptyParagraphNodeDecorations: () => Decoration;
33
+ export declare const shouldDescendIntoNode: (node: PMNode) => boolean;
33
34
  export declare const nodeDecorations: (newState: EditorState, from?: number, to?: number) => Decoration[];
34
35
  export declare const dragHandleDecoration: (api: ExtractInjectionAPI<BlockControlsPlugin>, formatMessage: IntlShape['formatMessage'], pos: number, anchorName: string, nodeType: string, handleOptions?: HandleOptions) => Decoration;
@@ -3,7 +3,7 @@ import { type IntlShape } from 'react-intl-next';
3
3
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
4
4
  import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
5
5
  import type { BlockControlsPlugin } from '../types';
6
- export type DropTargetStyle = 'default' | 'fullHeight';
6
+ export type DropTargetStyle = 'default' | 'remainingHeight';
7
7
  export type DropTargetProps = {
8
8
  api: ExtractInjectionAPI<BlockControlsPlugin> | undefined;
9
9
  getPos: () => number | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-block-controls",
3
- "version": "2.7.1",
3
+ "version": "2.9.0",
4
4
  "description": "Block controls plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -30,8 +30,8 @@
30
30
  ".": "./src/index.ts"
31
31
  },
32
32
  "dependencies": {
33
- "@atlaskit/adf-schema": "^42.0.2",
34
- "@atlaskit/editor-common": "^93.6.0",
33
+ "@atlaskit/adf-schema": "^42.3.1",
34
+ "@atlaskit/editor-common": "^94.0.0",
35
35
  "@atlaskit/editor-plugin-accessibility-utils": "^1.2.0",
36
36
  "@atlaskit/editor-plugin-analytics": "^1.10.0",
37
37
  "@atlaskit/editor-plugin-editor-disabled": "^1.3.0",
@@ -41,14 +41,14 @@
41
41
  "@atlaskit/editor-prosemirror": "6.0.0",
42
42
  "@atlaskit/editor-shared-styles": "^3.0.0",
43
43
  "@atlaskit/editor-tables": "^2.8.0",
44
- "@atlaskit/icon": "^22.22.0",
44
+ "@atlaskit/icon": "^22.23.0",
45
45
  "@atlaskit/platform-feature-flags": "^0.3.0",
46
46
  "@atlaskit/pragmatic-drag-and-drop": "^1.4.0",
47
47
  "@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^1.4.0",
48
48
  "@atlaskit/pragmatic-drag-and-drop-react-drop-indicator": "^1.1.0",
49
49
  "@atlaskit/primitives": "^12.2.0",
50
50
  "@atlaskit/theme": "^14.0.0",
51
- "@atlaskit/tmp-editor-statsig": "^2.6.0",
51
+ "@atlaskit/tmp-editor-statsig": "^2.7.0",
52
52
  "@atlaskit/tokens": "^2.0.0",
53
53
  "@atlaskit/tooltip": "^18.8.0",
54
54
  "@babel/runtime": "^7.0.0",