@atlaskit/editor-plugin-block-controls 2.12.1 → 2.13.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,19 @@
1
1
  # @atlaskit/editor-plugin-block-controls
2
2
 
3
+ ## 2.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#157826](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/157826)
8
+ [`130b9c5dcd038`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/130b9c5dcd038) -
9
+ [ED-25054] Support drag and drop a non layout column node in between layout columns
10
+
11
+ ## 2.12.2
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies
16
+
3
17
  ## 2.12.1
4
18
 
5
19
  ### Patch Changes
@@ -32,86 +32,123 @@ var createNewLayout = function createNewLayout(schema, layoutContents) {
32
32
  }
33
33
  return null;
34
34
  };
35
- var moveToLayout = exports.moveToLayout = function moveToLayout(api) {
36
- return function (from, to, position) {
37
- return function (_ref2) {
38
- var tr = _ref2.tr;
39
- // unable to drag a node to itself.
40
- if (from === to) {
41
- return tr;
35
+ var updateColumnWidths = function updateColumnWidths(tr, layoutNode, layoutNodePos, newColumnWidth) {
36
+ if (newColumnWidth) {
37
+ layoutNode.content.forEach(function (node, offset) {
38
+ if (node.type.name === 'layoutColumn') {
39
+ tr = tr.setNodeAttribute(layoutNodePos + offset + 1, 'width', newColumnWidth);
42
40
  }
43
- var _ref3 = tr.doc.type.schema.nodes || {},
44
- layoutSection = _ref3.layoutSection,
45
- layoutColumn = _ref3.layoutColumn,
46
- doc = _ref3.doc;
47
- var _ref4 = tr.doc.type.schema.marks || {},
48
- breakout = _ref4.breakout;
41
+ });
42
+ }
43
+ return tr;
44
+ };
49
45
 
50
- // layout plugin does not exist
51
- if (!layoutSection || !layoutColumn) {
52
- return tr;
53
- }
54
- var $to = tr.doc.resolve(to);
46
+ /**
47
+ * Insert a node into an existing layout at position `to` and delete the node
48
+ */
49
+ var moveNode = function moveNode(from, to, newNode, sourceNodeSize, tr) {
50
+ tr.insert(to, newNode).setSelection(new _state.NodeSelection(tr.doc.resolve(to))).scrollIntoView();
51
+ var mappedFrom = tr.mapping.map(from);
52
+ var mappedFromEnd = mappedFrom + sourceNodeSize;
53
+ tr.delete(mappedFrom, mappedFromEnd);
54
+ return tr;
55
+ };
56
+ var moveToExistingLayout = function moveToExistingLayout(toLayout, toLayoutPos, sourceNode, from, to, tr) {
57
+ if (toLayout.childCount < _consts.MAX_LAYOUT_COLUMN_SUPPORTED) {
58
+ var newColumnWidth = _consts2.DEFAULT_COLUMN_DISTRIBUTIONS[toLayout.childCount + 1];
59
+ updateColumnWidths(tr, toLayout, toLayoutPos, newColumnWidth);
60
+ var _ref2 = tr.doc.type.schema.nodes || {},
61
+ layoutColumn = _ref2.layoutColumn;
62
+ moveNode(from, to, layoutColumn.create({
63
+ width: newColumnWidth
64
+ }, sourceNode), sourceNode.nodeSize, tr);
65
+ }
66
+ return tr;
67
+ };
55
68
 
56
- // invalid to position or not top level.
57
- if (!$to.nodeAfter || $to.parent.type !== doc) {
58
- return tr;
59
- }
60
- var $from = tr.doc.resolve(from);
69
+ /**
70
+ * Check if the node at `from` can be moved to node at `to` to create/expand a layout.
71
+ * Returns the source and destination nodes and positions if it's a valid move, otherwise, undefined
72
+ */
73
+ var canMoveToLayout = function canMoveToLayout(from, to, tr) {
74
+ if (from === to) {
75
+ return;
76
+ }
77
+ var _ref3 = tr.doc.type.schema.nodes || {},
78
+ layoutSection = _ref3.layoutSection,
79
+ layoutColumn = _ref3.layoutColumn,
80
+ doc = _ref3.doc;
61
81
 
62
- // invalid from position or dragging a layout
63
- if (!$from.nodeAfter || $from.nodeAfter.type === layoutSection) {
82
+ // layout plugin does not exist
83
+ if (!layoutSection || !layoutColumn) {
84
+ return;
85
+ }
86
+ var $to = tr.doc.resolve(to);
87
+
88
+ // drop at invalid position, not top level, or not a layout column
89
+ if (!$to.nodeAfter || ![doc, layoutSection].includes($to.parent.type)) {
90
+ return;
91
+ }
92
+ var $from = tr.doc.resolve(from);
93
+
94
+ // invalid from position or dragging a layout
95
+ if (!$from.nodeAfter || $from.nodeAfter.type === layoutSection) {
96
+ return;
97
+ }
98
+ var toNode = $to.nodeAfter;
99
+ var fromNode = $from.nodeAfter;
100
+ return {
101
+ toNode: toNode,
102
+ fromNode: fromNode,
103
+ $from: $from,
104
+ $to: $to
105
+ };
106
+ };
107
+ var moveToLayout = exports.moveToLayout = function moveToLayout(api) {
108
+ return function (from, to, options) {
109
+ return function (_ref4) {
110
+ var tr = _ref4.tr;
111
+ var canMove = canMoveToLayout(from, to, tr);
112
+ if (!canMove) {
64
113
  return tr;
65
114
  }
66
- var toNode = $to.nodeAfter;
67
- var fromNode = $from.nodeAfter;
115
+ var toNode = canMove.toNode,
116
+ fromNode = canMove.fromNode,
117
+ $from = canMove.$from,
118
+ $to = canMove.$to;
119
+ var _ref5 = tr.doc.type.schema.nodes || {},
120
+ layoutSection = _ref5.layoutSection,
121
+ layoutColumn = _ref5.layoutColumn;
122
+ var _ref6 = tr.doc.type.schema.marks || {},
123
+ breakout = _ref6.breakout;
124
+ var fromNodeWithoutBreakout = fromNode;
68
125
 
69
126
  // remove breakout from node;
70
127
  if (breakout && $from.nodeAfter && $from.nodeAfter.marks.some(function (m) {
71
128
  return m.type === breakout;
72
129
  })) {
73
- tr = tr.removeNodeMark(from, breakout);
130
+ tr.removeNodeMark(from, breakout);
131
+ // resolve again the source node after node updated (remove breakout marks)
132
+ fromNodeWithoutBreakout = tr.doc.resolve(from).nodeAfter;
74
133
  }
75
- if ($to.nodeAfter.type === layoutSection) {
76
- var existingLayoutNode = $to.nodeAfter;
77
- if (existingLayoutNode.childCount < _consts.MAX_LAYOUT_COLUMN_SUPPORTED) {
78
- var newColumnWidth = _consts2.DEFAULT_COLUMN_DISTRIBUTIONS[existingLayoutNode.childCount + 1];
79
- if (newColumnWidth) {
80
- existingLayoutNode.content.forEach(function (node, offset) {
81
- if (node.type === layoutColumn) {
82
- tr = tr.setNodeAttribute(to + offset + 1, 'width', newColumnWidth);
83
- }
84
- });
85
- }
86
- var toPos = position === 'left' ? to + 1 : to + existingLayoutNode.nodeSize - 1;
87
- tr = tr.insert(toPos,
88
- // resolve again the source node after node updated (remove breakout marks)
89
- layoutColumn.create({
90
- width: newColumnWidth
91
- }, tr.doc.resolve(from).nodeAfter));
92
- tr = tr.setSelection(new _state.NodeSelection(tr.doc.resolve(toPos)));
93
- var mappedFrom = tr.mapping.map(from);
94
- var mappedFromEnd = mappedFrom + fromNode.nodeSize;
95
- tr = tr.delete(mappedFrom, mappedFromEnd);
96
- tr = tr.scrollIntoView();
97
- return tr;
98
- }
134
+ if (!fromNodeWithoutBreakout) {
99
135
  return tr;
136
+ }
137
+ if (toNode.type === layoutSection) {
138
+ var toPos = options !== null && options !== void 0 && options.moveToEnd ? to + toNode.nodeSize - 1 : to + 1;
139
+ return moveToExistingLayout(toNode, to, fromNodeWithoutBreakout, from, toPos, tr);
140
+ } else if (toNode.type === layoutColumn) {
141
+ var toLayout = $to.parent;
142
+ var toLayoutPos = to - $to.parentOffset - 1;
143
+ var _toPos = options !== null && options !== void 0 && options.moveToEnd ? to + toNode.nodeSize : to;
144
+ return moveToExistingLayout(toLayout, toLayoutPos, fromNodeWithoutBreakout, from, _toPos, tr);
100
145
  } else {
101
- // resolve again the source node after node updated (remove breakout marks)
102
- var newFromNode = tr.doc.resolve(from).nodeAfter;
103
- if (!newFromNode) {
104
- return tr;
105
- }
106
- var layoutContents = position === 'left' ? [newFromNode, toNode] : [toNode, newFromNode];
146
+ var layoutContents = options !== null && options !== void 0 && options.moveToEnd ? [toNode, fromNodeWithoutBreakout] : [fromNodeWithoutBreakout, toNode];
107
147
  var newLayout = createNewLayout(tr.doc.type.schema, layoutContents);
108
148
  if (newLayout) {
109
- tr = tr.delete(from, from + fromNode.nodeSize);
149
+ tr.delete(from, from + fromNode.nodeSize);
110
150
  var mappedTo = tr.mapping.map(to);
111
- tr = tr.delete(mappedTo, mappedTo + toNode.nodeSize);
112
- tr = tr.insert(mappedTo, newLayout); // insert the content at the new position
113
- tr = tr.setSelection(new _state.NodeSelection(tr.doc.resolve(mappedTo)));
114
- tr = tr.scrollIntoView();
151
+ tr.delete(mappedTo, mappedTo + toNode.nodeSize).insert(mappedTo, newLayout).setSelection(new _state.NodeSelection(tr.doc.resolve(mappedTo))).scrollIntoView();
115
152
  }
116
153
  return tr;
117
154
  }
@@ -37,8 +37,8 @@ var dropTargetLayoutHintStyle = (0, _react2.css)({
37
37
  width: 0
38
38
  });
39
39
  var DropTargetLayout = exports.DropTargetLayout = function DropTargetLayout(props) {
40
- var _api$blockControls2;
41
- var api = props.api;
40
+ var api = props.api,
41
+ getPos = props.getPos;
42
42
  var ref = (0, _react.useRef)(null);
43
43
  var _useState = (0, _react.useState)(false),
44
44
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
@@ -51,7 +51,13 @@ var DropTargetLayout = exports.DropTargetLayout = function DropTargetLayout(prop
51
51
  if (!activeNode) {
52
52
  return;
53
53
  }
54
- }, [api === null || api === void 0 || (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 ? void 0 : _api$blockControls2.sharedState]);
54
+ var to = getPos();
55
+ if (to !== undefined) {
56
+ var _api$core, _api$blockControls2;
57
+ var from = activeNode.pos;
58
+ 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.moveToLayout(from, to));
59
+ }
60
+ }, [api, getPos]);
55
61
  (0, _react.useEffect)(function () {
56
62
  if (ref.current) {
57
63
  return (0, _adapter.dropTargetForElements)({
@@ -141,7 +141,9 @@ var InlineDropTarget = exports.InlineDropTarget = function InlineDropTarget(_ref
141
141
  if (activeNode && toPos !== undefined) {
142
142
  var _api$core, _api$blockControls2;
143
143
  var start = activeNode.pos;
144
- 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.moveToLayout(start, toPos, position));
144
+ 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.moveToLayout(start, toPos, {
145
+ moveToEnd: position === 'right'
146
+ }));
145
147
  }
146
148
  }, [api, getPos, position]);
147
149
  return (0, _react2.jsx)(_react.Fragment, null, (0, _react2.jsx)("div", {
@@ -27,85 +27,126 @@ const createNewLayout = (schema, layoutContents) => {
27
27
  }
28
28
  return null;
29
29
  };
30
- export const moveToLayout = api => (from, to, position) => ({
31
- tr
32
- }) => {
33
- // unable to drag a node to itself.
30
+ const updateColumnWidths = (tr, layoutNode, layoutNodePos, newColumnWidth) => {
31
+ if (newColumnWidth) {
32
+ layoutNode.content.forEach((node, offset) => {
33
+ if (node.type.name === 'layoutColumn') {
34
+ tr = tr.setNodeAttribute(layoutNodePos + offset + 1, 'width', newColumnWidth);
35
+ }
36
+ });
37
+ }
38
+ return tr;
39
+ };
40
+
41
+ /**
42
+ * Insert a node into an existing layout at position `to` and delete the node
43
+ */
44
+ const moveNode = (from, to, newNode, sourceNodeSize, tr) => {
45
+ tr.insert(to, newNode).setSelection(new NodeSelection(tr.doc.resolve(to))).scrollIntoView();
46
+ const mappedFrom = tr.mapping.map(from);
47
+ const mappedFromEnd = mappedFrom + sourceNodeSize;
48
+ tr.delete(mappedFrom, mappedFromEnd);
49
+ return tr;
50
+ };
51
+ const moveToExistingLayout = (toLayout, toLayoutPos, sourceNode, from, to, tr) => {
52
+ if (toLayout.childCount < MAX_LAYOUT_COLUMN_SUPPORTED) {
53
+ const newColumnWidth = DEFAULT_COLUMN_DISTRIBUTIONS[toLayout.childCount + 1];
54
+ updateColumnWidths(tr, toLayout, toLayoutPos, newColumnWidth);
55
+ const {
56
+ layoutColumn
57
+ } = tr.doc.type.schema.nodes || {};
58
+ moveNode(from, to, layoutColumn.create({
59
+ width: newColumnWidth
60
+ }, sourceNode), sourceNode.nodeSize, tr);
61
+ }
62
+ return tr;
63
+ };
64
+
65
+ /**
66
+ * Check if the node at `from` can be moved to node at `to` to create/expand a layout.
67
+ * Returns the source and destination nodes and positions if it's a valid move, otherwise, undefined
68
+ */
69
+ const canMoveToLayout = (from, to, tr) => {
34
70
  if (from === to) {
35
- return tr;
71
+ return;
36
72
  }
37
73
  const {
38
74
  layoutSection,
39
75
  layoutColumn,
40
76
  doc
41
77
  } = tr.doc.type.schema.nodes || {};
42
- const {
43
- breakout
44
- } = tr.doc.type.schema.marks || {};
45
78
 
46
79
  // layout plugin does not exist
47
80
  if (!layoutSection || !layoutColumn) {
48
- return tr;
81
+ return;
49
82
  }
50
83
  const $to = tr.doc.resolve(to);
51
84
 
52
- // invalid to position or not top level.
53
- if (!$to.nodeAfter || $to.parent.type !== doc) {
54
- return tr;
85
+ // drop at invalid position, not top level, or not a layout column
86
+ if (!$to.nodeAfter || ![doc, layoutSection].includes($to.parent.type)) {
87
+ return;
55
88
  }
56
89
  const $from = tr.doc.resolve(from);
57
90
 
58
91
  // invalid from position or dragging a layout
59
92
  if (!$from.nodeAfter || $from.nodeAfter.type === layoutSection) {
60
- return tr;
93
+ return;
61
94
  }
62
95
  const toNode = $to.nodeAfter;
63
96
  const fromNode = $from.nodeAfter;
97
+ return {
98
+ toNode,
99
+ fromNode,
100
+ $from,
101
+ $to
102
+ };
103
+ };
104
+ export const moveToLayout = api => (from, to, options) => ({
105
+ tr
106
+ }) => {
107
+ const canMove = canMoveToLayout(from, to, tr);
108
+ if (!canMove) {
109
+ return tr;
110
+ }
111
+ const {
112
+ toNode,
113
+ fromNode,
114
+ $from,
115
+ $to
116
+ } = canMove;
117
+ const {
118
+ layoutSection,
119
+ layoutColumn
120
+ } = tr.doc.type.schema.nodes || {};
121
+ const {
122
+ breakout
123
+ } = tr.doc.type.schema.marks || {};
124
+ let fromNodeWithoutBreakout = fromNode;
64
125
 
65
126
  // remove breakout from node;
66
127
  if (breakout && $from.nodeAfter && $from.nodeAfter.marks.some(m => m.type === breakout)) {
67
- tr = tr.removeNodeMark(from, breakout);
128
+ tr.removeNodeMark(from, breakout);
129
+ // resolve again the source node after node updated (remove breakout marks)
130
+ fromNodeWithoutBreakout = tr.doc.resolve(from).nodeAfter;
68
131
  }
69
- if ($to.nodeAfter.type === layoutSection) {
70
- const existingLayoutNode = $to.nodeAfter;
71
- if (existingLayoutNode.childCount < MAX_LAYOUT_COLUMN_SUPPORTED) {
72
- const newColumnWidth = DEFAULT_COLUMN_DISTRIBUTIONS[existingLayoutNode.childCount + 1];
73
- if (newColumnWidth) {
74
- existingLayoutNode.content.forEach((node, offset) => {
75
- if (node.type === layoutColumn) {
76
- tr = tr.setNodeAttribute(to + offset + 1, 'width', newColumnWidth);
77
- }
78
- });
79
- }
80
- const toPos = position === 'left' ? to + 1 : to + existingLayoutNode.nodeSize - 1;
81
- tr = tr.insert(toPos,
82
- // resolve again the source node after node updated (remove breakout marks)
83
- layoutColumn.create({
84
- width: newColumnWidth
85
- }, tr.doc.resolve(from).nodeAfter));
86
- tr = tr.setSelection(new NodeSelection(tr.doc.resolve(toPos)));
87
- const mappedFrom = tr.mapping.map(from);
88
- const mappedFromEnd = mappedFrom + fromNode.nodeSize;
89
- tr = tr.delete(mappedFrom, mappedFromEnd);
90
- tr = tr.scrollIntoView();
91
- return tr;
92
- }
132
+ if (!fromNodeWithoutBreakout) {
93
133
  return tr;
134
+ }
135
+ if (toNode.type === layoutSection) {
136
+ const toPos = options !== null && options !== void 0 && options.moveToEnd ? to + toNode.nodeSize - 1 : to + 1;
137
+ return moveToExistingLayout(toNode, to, fromNodeWithoutBreakout, from, toPos, tr);
138
+ } else if (toNode.type === layoutColumn) {
139
+ const toLayout = $to.parent;
140
+ const toLayoutPos = to - $to.parentOffset - 1;
141
+ const toPos = options !== null && options !== void 0 && options.moveToEnd ? to + toNode.nodeSize : to;
142
+ return moveToExistingLayout(toLayout, toLayoutPos, fromNodeWithoutBreakout, from, toPos, tr);
94
143
  } else {
95
- // resolve again the source node after node updated (remove breakout marks)
96
- const newFromNode = tr.doc.resolve(from).nodeAfter;
97
- if (!newFromNode) {
98
- return tr;
99
- }
100
- const layoutContents = position === 'left' ? [newFromNode, toNode] : [toNode, newFromNode];
144
+ const layoutContents = options !== null && options !== void 0 && options.moveToEnd ? [toNode, fromNodeWithoutBreakout] : [fromNodeWithoutBreakout, toNode];
101
145
  const newLayout = createNewLayout(tr.doc.type.schema, layoutContents);
102
146
  if (newLayout) {
103
- tr = tr.delete(from, from + fromNode.nodeSize);
147
+ tr.delete(from, from + fromNode.nodeSize);
104
148
  const mappedTo = tr.mapping.map(to);
105
- tr = tr.delete(mappedTo, mappedTo + toNode.nodeSize);
106
- tr = tr.insert(mappedTo, newLayout); // insert the content at the new position
107
- tr = tr.setSelection(new NodeSelection(tr.doc.resolve(mappedTo)));
108
- tr = tr.scrollIntoView();
149
+ tr.delete(mappedTo, mappedTo + toNode.nodeSize).insert(mappedTo, newLayout).setSelection(new NodeSelection(tr.doc.resolve(mappedTo))).scrollIntoView();
109
150
  }
110
151
  return tr;
111
152
  }
@@ -29,9 +29,9 @@ const dropTargetLayoutHintStyle = css({
29
29
  width: 0
30
30
  });
31
31
  export const DropTargetLayout = props => {
32
- var _api$blockControls2;
33
32
  const {
34
- api
33
+ api,
34
+ getPos
35
35
  } = props;
36
36
  const ref = useRef(null);
37
37
  const [isDraggedOver, setIsDraggedOver] = useState(false);
@@ -43,7 +43,15 @@ export const DropTargetLayout = props => {
43
43
  if (!activeNode) {
44
44
  return;
45
45
  }
46
- }, [api === null || api === void 0 ? void 0 : (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 ? void 0 : _api$blockControls2.sharedState]);
46
+ const to = getPos();
47
+ if (to !== undefined) {
48
+ var _api$core, _api$blockControls2, _api$blockControls2$c;
49
+ const {
50
+ pos: from
51
+ } = activeNode;
52
+ 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.moveToLayout(from, to));
53
+ }
54
+ }, [api, getPos]);
47
55
  useEffect(() => {
48
56
  if (ref.current) {
49
57
  return dropTargetForElements({
@@ -134,7 +134,9 @@ export const InlineDropTarget = ({
134
134
  const {
135
135
  pos: start
136
136
  } = activeNode;
137
- 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.moveToLayout(start, toPos, position));
137
+ 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.moveToLayout(start, toPos, {
138
+ moveToEnd: position === 'right'
139
+ }));
138
140
  }
139
141
  }, [api, getPos, position]);
140
142
  return jsx(Fragment, null, jsx("div", {
@@ -26,86 +26,123 @@ var createNewLayout = function createNewLayout(schema, layoutContents) {
26
26
  }
27
27
  return null;
28
28
  };
29
- export var moveToLayout = function moveToLayout(api) {
30
- return function (from, to, position) {
31
- return function (_ref2) {
32
- var tr = _ref2.tr;
33
- // unable to drag a node to itself.
34
- if (from === to) {
35
- return tr;
29
+ var updateColumnWidths = function updateColumnWidths(tr, layoutNode, layoutNodePos, newColumnWidth) {
30
+ if (newColumnWidth) {
31
+ layoutNode.content.forEach(function (node, offset) {
32
+ if (node.type.name === 'layoutColumn') {
33
+ tr = tr.setNodeAttribute(layoutNodePos + offset + 1, 'width', newColumnWidth);
36
34
  }
37
- var _ref3 = tr.doc.type.schema.nodes || {},
38
- layoutSection = _ref3.layoutSection,
39
- layoutColumn = _ref3.layoutColumn,
40
- doc = _ref3.doc;
41
- var _ref4 = tr.doc.type.schema.marks || {},
42
- breakout = _ref4.breakout;
35
+ });
36
+ }
37
+ return tr;
38
+ };
43
39
 
44
- // layout plugin does not exist
45
- if (!layoutSection || !layoutColumn) {
46
- return tr;
47
- }
48
- var $to = tr.doc.resolve(to);
40
+ /**
41
+ * Insert a node into an existing layout at position `to` and delete the node
42
+ */
43
+ var moveNode = function moveNode(from, to, newNode, sourceNodeSize, tr) {
44
+ tr.insert(to, newNode).setSelection(new NodeSelection(tr.doc.resolve(to))).scrollIntoView();
45
+ var mappedFrom = tr.mapping.map(from);
46
+ var mappedFromEnd = mappedFrom + sourceNodeSize;
47
+ tr.delete(mappedFrom, mappedFromEnd);
48
+ return tr;
49
+ };
50
+ var moveToExistingLayout = function moveToExistingLayout(toLayout, toLayoutPos, sourceNode, from, to, tr) {
51
+ if (toLayout.childCount < MAX_LAYOUT_COLUMN_SUPPORTED) {
52
+ var newColumnWidth = DEFAULT_COLUMN_DISTRIBUTIONS[toLayout.childCount + 1];
53
+ updateColumnWidths(tr, toLayout, toLayoutPos, newColumnWidth);
54
+ var _ref2 = tr.doc.type.schema.nodes || {},
55
+ layoutColumn = _ref2.layoutColumn;
56
+ moveNode(from, to, layoutColumn.create({
57
+ width: newColumnWidth
58
+ }, sourceNode), sourceNode.nodeSize, tr);
59
+ }
60
+ return tr;
61
+ };
49
62
 
50
- // invalid to position or not top level.
51
- if (!$to.nodeAfter || $to.parent.type !== doc) {
52
- return tr;
53
- }
54
- var $from = tr.doc.resolve(from);
63
+ /**
64
+ * Check if the node at `from` can be moved to node at `to` to create/expand a layout.
65
+ * Returns the source and destination nodes and positions if it's a valid move, otherwise, undefined
66
+ */
67
+ var canMoveToLayout = function canMoveToLayout(from, to, tr) {
68
+ if (from === to) {
69
+ return;
70
+ }
71
+ var _ref3 = tr.doc.type.schema.nodes || {},
72
+ layoutSection = _ref3.layoutSection,
73
+ layoutColumn = _ref3.layoutColumn,
74
+ doc = _ref3.doc;
55
75
 
56
- // invalid from position or dragging a layout
57
- if (!$from.nodeAfter || $from.nodeAfter.type === layoutSection) {
76
+ // layout plugin does not exist
77
+ if (!layoutSection || !layoutColumn) {
78
+ return;
79
+ }
80
+ var $to = tr.doc.resolve(to);
81
+
82
+ // drop at invalid position, not top level, or not a layout column
83
+ if (!$to.nodeAfter || ![doc, layoutSection].includes($to.parent.type)) {
84
+ return;
85
+ }
86
+ var $from = tr.doc.resolve(from);
87
+
88
+ // invalid from position or dragging a layout
89
+ if (!$from.nodeAfter || $from.nodeAfter.type === layoutSection) {
90
+ return;
91
+ }
92
+ var toNode = $to.nodeAfter;
93
+ var fromNode = $from.nodeAfter;
94
+ return {
95
+ toNode: toNode,
96
+ fromNode: fromNode,
97
+ $from: $from,
98
+ $to: $to
99
+ };
100
+ };
101
+ export var moveToLayout = function moveToLayout(api) {
102
+ return function (from, to, options) {
103
+ return function (_ref4) {
104
+ var tr = _ref4.tr;
105
+ var canMove = canMoveToLayout(from, to, tr);
106
+ if (!canMove) {
58
107
  return tr;
59
108
  }
60
- var toNode = $to.nodeAfter;
61
- var fromNode = $from.nodeAfter;
109
+ var toNode = canMove.toNode,
110
+ fromNode = canMove.fromNode,
111
+ $from = canMove.$from,
112
+ $to = canMove.$to;
113
+ var _ref5 = tr.doc.type.schema.nodes || {},
114
+ layoutSection = _ref5.layoutSection,
115
+ layoutColumn = _ref5.layoutColumn;
116
+ var _ref6 = tr.doc.type.schema.marks || {},
117
+ breakout = _ref6.breakout;
118
+ var fromNodeWithoutBreakout = fromNode;
62
119
 
63
120
  // remove breakout from node;
64
121
  if (breakout && $from.nodeAfter && $from.nodeAfter.marks.some(function (m) {
65
122
  return m.type === breakout;
66
123
  })) {
67
- tr = tr.removeNodeMark(from, breakout);
124
+ tr.removeNodeMark(from, breakout);
125
+ // resolve again the source node after node updated (remove breakout marks)
126
+ fromNodeWithoutBreakout = tr.doc.resolve(from).nodeAfter;
68
127
  }
69
- if ($to.nodeAfter.type === layoutSection) {
70
- var existingLayoutNode = $to.nodeAfter;
71
- if (existingLayoutNode.childCount < MAX_LAYOUT_COLUMN_SUPPORTED) {
72
- var newColumnWidth = DEFAULT_COLUMN_DISTRIBUTIONS[existingLayoutNode.childCount + 1];
73
- if (newColumnWidth) {
74
- existingLayoutNode.content.forEach(function (node, offset) {
75
- if (node.type === layoutColumn) {
76
- tr = tr.setNodeAttribute(to + offset + 1, 'width', newColumnWidth);
77
- }
78
- });
79
- }
80
- var toPos = position === 'left' ? to + 1 : to + existingLayoutNode.nodeSize - 1;
81
- tr = tr.insert(toPos,
82
- // resolve again the source node after node updated (remove breakout marks)
83
- layoutColumn.create({
84
- width: newColumnWidth
85
- }, tr.doc.resolve(from).nodeAfter));
86
- tr = tr.setSelection(new NodeSelection(tr.doc.resolve(toPos)));
87
- var mappedFrom = tr.mapping.map(from);
88
- var mappedFromEnd = mappedFrom + fromNode.nodeSize;
89
- tr = tr.delete(mappedFrom, mappedFromEnd);
90
- tr = tr.scrollIntoView();
91
- return tr;
92
- }
128
+ if (!fromNodeWithoutBreakout) {
93
129
  return tr;
130
+ }
131
+ if (toNode.type === layoutSection) {
132
+ var toPos = options !== null && options !== void 0 && options.moveToEnd ? to + toNode.nodeSize - 1 : to + 1;
133
+ return moveToExistingLayout(toNode, to, fromNodeWithoutBreakout, from, toPos, tr);
134
+ } else if (toNode.type === layoutColumn) {
135
+ var toLayout = $to.parent;
136
+ var toLayoutPos = to - $to.parentOffset - 1;
137
+ var _toPos = options !== null && options !== void 0 && options.moveToEnd ? to + toNode.nodeSize : to;
138
+ return moveToExistingLayout(toLayout, toLayoutPos, fromNodeWithoutBreakout, from, _toPos, tr);
94
139
  } else {
95
- // resolve again the source node after node updated (remove breakout marks)
96
- var newFromNode = tr.doc.resolve(from).nodeAfter;
97
- if (!newFromNode) {
98
- return tr;
99
- }
100
- var layoutContents = position === 'left' ? [newFromNode, toNode] : [toNode, newFromNode];
140
+ var layoutContents = options !== null && options !== void 0 && options.moveToEnd ? [toNode, fromNodeWithoutBreakout] : [fromNodeWithoutBreakout, toNode];
101
141
  var newLayout = createNewLayout(tr.doc.type.schema, layoutContents);
102
142
  if (newLayout) {
103
- tr = tr.delete(from, from + fromNode.nodeSize);
143
+ tr.delete(from, from + fromNode.nodeSize);
104
144
  var mappedTo = tr.mapping.map(to);
105
- tr = tr.delete(mappedTo, mappedTo + toNode.nodeSize);
106
- tr = tr.insert(mappedTo, newLayout); // insert the content at the new position
107
- tr = tr.setSelection(new NodeSelection(tr.doc.resolve(mappedTo)));
108
- tr = tr.scrollIntoView();
145
+ tr.delete(mappedTo, mappedTo + toNode.nodeSize).insert(mappedTo, newLayout).setSelection(new NodeSelection(tr.doc.resolve(mappedTo))).scrollIntoView();
109
146
  }
110
147
  return tr;
111
148
  }
@@ -30,8 +30,8 @@ var dropTargetLayoutHintStyle = css({
30
30
  width: 0
31
31
  });
32
32
  export var DropTargetLayout = function DropTargetLayout(props) {
33
- var _api$blockControls2;
34
- var api = props.api;
33
+ var api = props.api,
34
+ getPos = props.getPos;
35
35
  var ref = useRef(null);
36
36
  var _useState = useState(false),
37
37
  _useState2 = _slicedToArray(_useState, 2),
@@ -44,7 +44,13 @@ export var DropTargetLayout = function DropTargetLayout(props) {
44
44
  if (!activeNode) {
45
45
  return;
46
46
  }
47
- }, [api === null || api === void 0 || (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 ? void 0 : _api$blockControls2.sharedState]);
47
+ var to = getPos();
48
+ if (to !== undefined) {
49
+ var _api$core, _api$blockControls2;
50
+ var from = activeNode.pos;
51
+ 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.moveToLayout(from, to));
52
+ }
53
+ }, [api, getPos]);
48
54
  useEffect(function () {
49
55
  if (ref.current) {
50
56
  return dropTargetForElements({
@@ -133,7 +133,9 @@ export var InlineDropTarget = function InlineDropTarget(_ref) {
133
133
  if (activeNode && toPos !== undefined) {
134
134
  var _api$core, _api$blockControls2;
135
135
  var start = activeNode.pos;
136
- 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.moveToLayout(start, toPos, position));
136
+ 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.moveToLayout(start, toPos, {
137
+ moveToEnd: position === 'right'
138
+ }));
137
139
  }
138
140
  }, [api, getPos, position]);
139
141
  return jsx(Fragment, null, jsx("div", {
@@ -1,3 +1,5 @@
1
1
  import type { EditorCommand, ExtractInjectionAPI } from '@atlaskit/editor-common/types';
2
2
  import type { BlockControlsPlugin } from '../types';
3
- export declare const moveToLayout: (api?: ExtractInjectionAPI<BlockControlsPlugin>) => (from: number, to: number, position: 'left' | 'right') => EditorCommand;
3
+ export declare const moveToLayout: (api?: ExtractInjectionAPI<BlockControlsPlugin>) => (from: number, to: number, options?: {
4
+ moveToEnd?: boolean;
5
+ }) => EditorCommand;
@@ -58,7 +58,15 @@ export type BlockControlsPlugin = NextEditorPlugin<'blockControls', {
58
58
  ];
59
59
  sharedState: BlockControlsSharedState;
60
60
  commands: {
61
- moveToLayout: (start: number, to: number, position: 'left' | 'right') => EditorCommand;
61
+ /**
62
+ * Move a node before (unless `moveToEnd` is set) another node to expand a layout or create a new layout
63
+ * @param from position of the node to be moved
64
+ * @param to position of the layout/layout column/node to move the node to
65
+ * @param options moveToEnd: move the node to after the layout/layout column/another node
66
+ */
67
+ moveToLayout: (start: number, to: number, options?: {
68
+ moveToEnd?: boolean;
69
+ }) => EditorCommand;
62
70
  moveNode: (start: number, to: number, inputMethod?: MoveNodeMethod, formatMessage?: IntlShape['formatMessage']) => EditorCommand;
63
71
  showDragHandleAt: (pos: number, anchorName: string, nodeType: string, handleOptions?: HandleOptions) => EditorCommand;
64
72
  setNodeDragged: (getPos: () => number | undefined, anchorName: string, nodeType: string) => EditorCommand;
@@ -1,3 +1,5 @@
1
1
  import type { EditorCommand, ExtractInjectionAPI } from '@atlaskit/editor-common/types';
2
2
  import type { BlockControlsPlugin } from '../types';
3
- export declare const moveToLayout: (api?: ExtractInjectionAPI<BlockControlsPlugin>) => (from: number, to: number, position: 'left' | 'right') => EditorCommand;
3
+ export declare const moveToLayout: (api?: ExtractInjectionAPI<BlockControlsPlugin>) => (from: number, to: number, options?: {
4
+ moveToEnd?: boolean;
5
+ }) => EditorCommand;
@@ -58,7 +58,15 @@ export type BlockControlsPlugin = NextEditorPlugin<'blockControls', {
58
58
  ];
59
59
  sharedState: BlockControlsSharedState;
60
60
  commands: {
61
- moveToLayout: (start: number, to: number, position: 'left' | 'right') => EditorCommand;
61
+ /**
62
+ * Move a node before (unless `moveToEnd` is set) another node to expand a layout or create a new layout
63
+ * @param from position of the node to be moved
64
+ * @param to position of the layout/layout column/node to move the node to
65
+ * @param options moveToEnd: move the node to after the layout/layout column/another node
66
+ */
67
+ moveToLayout: (start: number, to: number, options?: {
68
+ moveToEnd?: boolean;
69
+ }) => EditorCommand;
62
70
  moveNode: (start: number, to: number, inputMethod?: MoveNodeMethod, formatMessage?: IntlShape['formatMessage']) => EditorCommand;
63
71
  showDragHandleAt: (pos: number, anchorName: string, nodeType: string, handleOptions?: HandleOptions) => EditorCommand;
64
72
  setNodeDragged: (getPos: () => number | undefined, anchorName: string, nodeType: string) => EditorCommand;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-block-controls",
3
- "version": "2.12.1",
3
+ "version": "2.13.0",
4
4
  "description": "Block controls plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -46,7 +46,7 @@
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
- "@atlaskit/primitives": "^12.2.0",
49
+ "@atlaskit/primitives": "^13.0.0",
50
50
  "@atlaskit/theme": "^14.0.0",
51
51
  "@atlaskit/tmp-editor-statsig": "^2.10.0",
52
52
  "@atlaskit/tokens": "^2.0.0",