@atlaskit/editor-plugin-block-controls 1.2.0 → 1.3.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,23 +1,37 @@
1
1
  # @atlaskit/editor-plugin-block-controls
2
2
 
3
+ ## 1.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#95811](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/95811)
8
+ [`115361312a0e`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/115361312a0e) -
9
+ move drag position data to plugin state and map positions for collab support
10
+
3
11
  ## 1.2.0
4
12
 
5
13
  ### Minor Changes
6
14
 
7
- - [#93512](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/93512) [`15155cd6eb61`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/15155cd6eb61) - add basic dnd behaviour to block controls plugin
15
+ - [#93512](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/93512)
16
+ [`15155cd6eb61`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/15155cd6eb61) -
17
+ add basic dnd behaviour to block controls plugin
8
18
 
9
19
  ## 1.1.0
10
20
 
11
21
  ### Minor Changes
12
22
 
13
- - [#91934](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/91934) [`b76a78c6a199`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/b76a78c6a199) - bumped editor-prosemirror version to 4.0.0
23
+ - [#91934](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/91934)
24
+ [`b76a78c6a199`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/b76a78c6a199) -
25
+ bumped editor-prosemirror version to 4.0.0
14
26
 
15
27
  ### Patch Changes
16
28
 
17
- - Updated dependencies
29
+ - Updated dependencies
18
30
 
19
31
  ## 1.0.0
20
32
 
21
33
  ### Major Changes
22
34
 
23
- - [#91350](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/91350) [`f27c4b41ebed`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/f27c4b41ebed) - Adds new Editor Block Controls plugin
35
+ - [#91350](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/91350)
36
+ [`f27c4b41ebed`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/f27c4b41ebed) -
37
+ Adds new Editor Block Controls plugin
@@ -22,25 +22,31 @@ var blockControlsPlugin = exports.blockControlsPlugin = function blockControlsPl
22
22
  }];
23
23
  },
24
24
  commands: {
25
- moveNode: function moveNode(start, end, to) {
25
+ moveNode: function moveNode(start, to) {
26
26
  return function (_ref2) {
27
- var _api$core;
27
+ var _node$nodeSize, _api$core;
28
28
  var tr = _ref2.tr;
29
- var node = tr.doc.content.cut(start, end); // cut the content
29
+ var node = tr.doc.nodeAt(start);
30
+ var size = (_node$nodeSize = node === null || node === void 0 ? void 0 : node.nodeSize) !== null && _node$nodeSize !== void 0 ? _node$nodeSize : 1;
31
+ var end = start + size;
32
+ var nodeCopy = tr.doc.content.cut(start, end); // cut the content
30
33
  tr.delete(start, end); // delete the content from the original position
31
- tr.insert(tr.mapping.map(to), node); // insert the content at the new position
34
+ var mappedTo = tr.mapping.map(to);
35
+ tr.insert(mappedTo, nodeCopy); // insert the content at the new position
32
36
  api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.focus();
33
37
  return tr;
34
38
  };
35
39
  }
36
40
  },
37
41
  getSharedState: function getSharedState(editorState) {
38
- var _key$getState$isMenuO, _key$getState;
42
+ var _key$getState$isMenuO, _key$getState, _key$getState$activeN, _key$getState2, _key$getState$decorat, _key$getState3;
39
43
  if (!editorState) {
40
44
  return undefined;
41
45
  }
42
46
  return {
43
- isMenuOpen: (_key$getState$isMenuO = (_key$getState = _main.key.getState(editorState)) === null || _key$getState === void 0 ? void 0 : _key$getState.isMenuOpen) !== null && _key$getState$isMenuO !== void 0 ? _key$getState$isMenuO : false
47
+ isMenuOpen: (_key$getState$isMenuO = (_key$getState = _main.key.getState(editorState)) === null || _key$getState === void 0 ? void 0 : _key$getState.isMenuOpen) !== null && _key$getState$isMenuO !== void 0 ? _key$getState$isMenuO : false,
48
+ activeNode: (_key$getState$activeN = (_key$getState2 = _main.key.getState(editorState)) === null || _key$getState2 === void 0 ? void 0 : _key$getState2.activeNode) !== null && _key$getState$activeN !== void 0 ? _key$getState$activeN : null,
49
+ decorationState: (_key$getState$decorat = (_key$getState3 = _main.key.getState(editorState)) === null || _key$getState3 === void 0 ? void 0 : _key$getState3.decorationState) !== null && _key$getState$decorat !== void 0 ? _key$getState$decorat : []
44
50
  };
45
51
  },
46
52
  contentComponent: function contentComponent() {
@@ -11,38 +11,59 @@ var _view = require("@atlaskit/editor-prosemirror/view");
11
11
  var _dragHandle = require("../ui/drag-handle");
12
12
  var _dropTarget = require("../ui/drop-target");
13
13
  var dropTargetDecorations = exports.dropTargetDecorations = function dropTargetDecorations(oldState, newState, api) {
14
- var decorations = [];
15
- oldState.doc.nodesBetween(0, newState.doc.nodeSize - 2, function (node, pos) {
16
- decorations.push(_view.Decoration.widget(pos + node.nodeSize, function () {
14
+ var decs = [];
15
+ // Decoration state is used to keep track of the position of the drop targets
16
+ // and allows us to easily map the updated position in the plugin apply method.
17
+ var decorationState = [];
18
+ oldState.doc.nodesBetween(0, newState.doc.nodeSize - 2, function (_node, pos, _parent, index) {
19
+ decorationState.push({
20
+ index: index,
21
+ pos: pos
22
+ });
23
+ decs.push(_view.Decoration.widget(pos, function () {
17
24
  var element = document.createElement('div');
18
25
  _reactDom.default.render( /*#__PURE__*/(0, _react.createElement)(_dropTarget.DropTarget, {
19
26
  api: api,
20
- pos: pos + node.nodeSize
27
+ index: index
21
28
  }), element);
22
29
  return element;
23
30
  }));
24
31
  return false;
25
32
  });
26
- decorations.push(_view.Decoration.widget(0, function () {
33
+
34
+ /**
35
+ * We are adding a drop target at the end of the document because by default we
36
+ * draw all drop targets at the top of every node. It's better to draw the drop targets
37
+ * at the top of each node because that way we only need to know the start position of the
38
+ * node and not its size.
39
+ *
40
+ */
41
+ decorationState.push({
42
+ index: decorationState.length + 1,
43
+ pos: newState.doc.nodeSize - 2
44
+ });
45
+ decs.push(_view.Decoration.widget(newState.doc.nodeSize - 2, function () {
27
46
  var element = document.createElement('div');
28
47
  _reactDom.default.render( /*#__PURE__*/(0, _react.createElement)(_dropTarget.DropTarget, {
29
48
  api: api,
30
- pos: 0
49
+ index: decorationState.length
31
50
  }), element);
32
51
  return element;
33
52
  }));
34
- return decorations;
53
+ return {
54
+ decs: decs,
55
+ decorationState: decorationState
56
+ };
35
57
  };
36
58
  var dragHandleDecoration = exports.dragHandleDecoration = function dragHandleDecoration(oldState,
37
59
  // @ts-ignore
38
60
  meta, api) {
39
- return _view.DecorationSet.create(oldState.doc, [_view.Decoration.widget(meta.pos + meta.size, function (view, getPos) {
61
+ return _view.DecorationSet.create(oldState.doc, [_view.Decoration.widget(meta.pos, function (view, getPos) {
40
62
  var element = document.createElement('div');
41
63
  _reactDom.default.render( /*#__PURE__*/(0, _react.createElement)(_dragHandle.DragHandle, {
42
64
  dom: meta.dom,
43
65
  api: api,
44
- start: meta.pos,
45
- end: meta.pos + meta.size
66
+ start: meta.pos
46
67
  }), element);
47
68
  element.style.position = 'absolute';
48
69
  return element;
@@ -16,17 +16,23 @@ var createPlugin = exports.createPlugin = function createPlugin(api) {
16
16
  init: function init() {
17
17
  return {
18
18
  decorations: _view.DecorationSet.empty,
19
+ decorationState: [],
19
20
  activeNode: null,
20
21
  isDragging: false,
21
- isMenuOpen: false
22
+ isMenuOpen: false,
23
+ start: null,
24
+ end: null
22
25
  };
23
26
  },
24
27
  apply: function apply(tr, currentState, oldState, newState) {
25
- var _meta$isDragging;
28
+ var _decorationState, _meta$pos, _meta$isDragging;
29
+ // return currentState;
26
30
  var activeNode = currentState.activeNode,
27
31
  decorations = currentState.decorations,
28
- isMenuOpen = currentState.isMenuOpen;
32
+ isMenuOpen = currentState.isMenuOpen,
33
+ decorationState = currentState.decorationState;
29
34
  var meta = tr.getMeta(key);
35
+
30
36
  // Drag handle decoration
31
37
  if (meta && meta.pos !== (activeNode === null || activeNode === void 0 ? void 0 : activeNode.pos) && api) {
32
38
  decorations = (0, _decorations.dragHandleDecoration)(newState, meta, api);
@@ -34,20 +40,44 @@ var createPlugin = exports.createPlugin = function createPlugin(api) {
34
40
  // Drop target decorations
35
41
  if (meta !== null && meta !== void 0 && meta.isDragging && api) {
36
42
  decorations = _view.DecorationSet.create(newState.doc, []);
37
- var decs = (0, _decorations.dropTargetDecorations)(oldState, newState, api);
43
+ var _dropTargetDecoration = (0, _decorations.dropTargetDecorations)(oldState, newState, api),
44
+ decs = _dropTargetDecoration.decs,
45
+ updatedDecorationState = _dropTargetDecoration.decorationState;
46
+ decorationState = updatedDecorationState;
38
47
  decorations = decorations.add(newState.doc, decs);
39
48
  }
40
- // Remove target decorations when dragging is stopped
49
+
50
+ // Remove drop target decorations when dragging is stopped
41
51
  if ((meta === null || meta === void 0 ? void 0 : meta.isDragging) === false) {
42
52
  decorations = _view.DecorationSet.create(newState.doc, []);
43
53
  }
54
+
55
+ // Map decorations when the document changes
56
+ if (tr.docChanged && decorations !== _view.DecorationSet.empty) {
57
+ decorations = decorations.map(tr.mapping, tr.doc);
58
+ }
59
+
60
+ // Map drop target decoration positions when the document changes
61
+ if (tr.docChanged && currentState.isDragging) {
62
+ decorationState = decorationState.map(function (_ref) {
63
+ var index = _ref.index,
64
+ pos = _ref.pos;
65
+ return {
66
+ index: index,
67
+ pos: tr.mapping.map(pos)
68
+ };
69
+ });
70
+ }
71
+
72
+ // Map active node position when the document changes
73
+ var mappedActiveNodePos = tr.docChanged && activeNode ? tr.mapping.map(activeNode.pos) : activeNode === null || activeNode === void 0 ? void 0 : activeNode.pos;
44
74
  return {
45
75
  decorations: decorations,
76
+ decorationState: (_decorationState = decorationState) !== null && _decorationState !== void 0 ? _decorationState : currentState.decorationState,
46
77
  activeNode: {
47
- pos: meta === null || meta === void 0 ? void 0 : meta.pos,
48
- size: meta === null || meta === void 0 ? void 0 : meta.size
78
+ pos: (_meta$pos = meta === null || meta === void 0 ? void 0 : meta.pos) !== null && _meta$pos !== void 0 ? _meta$pos : mappedActiveNodePos
49
79
  },
50
- isDragging: (_meta$isDragging = meta === null || meta === void 0 ? void 0 : meta.isDragging) !== null && _meta$isDragging !== void 0 ? _meta$isDragging : false,
80
+ isDragging: (_meta$isDragging = meta === null || meta === void 0 ? void 0 : meta.isDragging) !== null && _meta$isDragging !== void 0 ? _meta$isDragging : currentState.isDragging,
51
81
  isMenuOpen: meta !== null && meta !== void 0 && meta.toggleMenu ? !isMenuOpen : isMenuOpen
52
82
  };
53
83
  }
@@ -79,11 +109,10 @@ var createPlugin = exports.createPlugin = function createPlugin(api) {
79
109
  if (!dom) {
80
110
  return;
81
111
  }
82
- api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref) {
83
- var tr = _ref.tr;
112
+ api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref2) {
113
+ var tr = _ref2.tr;
84
114
  return tr.setMeta(key, {
85
115
  pos: topLevelPos,
86
- size: topLevelNode.nodeSize,
87
116
  dom: dom,
88
117
  type: topLevelNode.type.name
89
118
  });
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.DragHandle = void 0;
7
7
  var _react = require("react");
8
8
  var _react2 = require("@emotion/react");
9
- var _combine = require("@atlaskit/pragmatic-drag-and-drop/combine");
10
9
  var _adapter = require("@atlaskit/pragmatic-drag-and-drop/element/adapter");
11
10
  var _setCustomNativeDragPreview = require("@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview");
12
11
  var _main = require("../pm-plugins/main");
@@ -16,6 +15,8 @@ var _dragPreview = require("./drag-preview");
16
15
 
17
16
  var styles = (0, _react2.css)({
18
17
  position: 'absolute',
18
+ // eslint-disable-next-line @atlaskit/design-system/ensure-design-token-usage
19
+ background: 'grey',
19
20
  zIndex: 1,
20
21
  height: _consts.DRAG_HANDLE_HEIGHT,
21
22
  width: _consts.DRAG_HANDLE_WIDTH,
@@ -24,8 +25,7 @@ var styles = (0, _react2.css)({
24
25
  var DragHandle = exports.DragHandle = function DragHandle(_ref) {
25
26
  var dom = _ref.dom,
26
27
  api = _ref.api,
27
- start = _ref.start,
28
- end = _ref.end;
28
+ start = _ref.start;
29
29
  var buttonRef = (0, _react.useRef)(null);
30
30
  var domRef = (0, _react.useRef)(dom);
31
31
  (0, _react.useEffect)(function () {
@@ -33,14 +33,8 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref) {
33
33
  if (!element) {
34
34
  return;
35
35
  }
36
- return (0, _combine.combine)((0, _adapter.draggable)({
36
+ return (0, _adapter.draggable)({
37
37
  element: element,
38
- getInitialData: function getInitialData() {
39
- return {
40
- start: start,
41
- end: end
42
- };
43
- },
44
38
  onGenerateDragPreview: function onGenerateDragPreview(_ref2) {
45
39
  var nativeSetDragImage = _ref2.nativeSetDragImage;
46
40
  (0, _setCustomNativeDragPreview.setCustomNativeDragPreview)({
@@ -64,7 +58,8 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref) {
64
58
  api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref4) {
65
59
  var tr = _ref4.tr;
66
60
  return tr.setMeta(_main.key, {
67
- isDragging: true
61
+ isDragging: true,
62
+ start: start
68
63
  });
69
64
  });
70
65
  },
@@ -77,15 +72,14 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref) {
77
72
  });
78
73
  });
79
74
  }
80
- }));
81
- }, [api, start, end]);
82
- return (0, _react2.jsx)("button", {
75
+ });
76
+ }, [api, start]);
77
+ return (0, _react2.jsx)("div", {
83
78
  css: styles,
84
79
  style: {
85
- bottom: dom.clientHeight / 2 - _consts.DRAG_HANDLE_HEIGHT / 2
80
+ top: dom.clientHeight / 2 - _consts.DRAG_HANDLE_HEIGHT / 2
86
81
  },
87
82
  ref: buttonRef,
88
- type: "button",
89
83
  onClick: function onClick() {
90
84
  var _api$core3;
91
85
  api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(function (_ref6) {
@@ -13,7 +13,7 @@ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return
13
13
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
14
14
  var DropTarget = exports.DropTarget = function DropTarget(_ref) {
15
15
  var api = _ref.api,
16
- pos = _ref.pos;
16
+ index = _ref.index;
17
17
  var ref = (0, _react.useRef)(null);
18
18
  var _useState = (0, _react.useState)(false),
19
19
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
@@ -35,23 +35,38 @@ var DropTarget = exports.DropTarget = function DropTarget(_ref) {
35
35
  onDragLeave: function onDragLeave() {
36
36
  return setIsDraggedOver(false);
37
37
  },
38
- onDrop: function onDrop(event) {
39
- var _api$core, _api$blockControls;
40
- var _ref2 = event.source.data,
41
- start = _ref2.start,
42
- end = _ref2.end;
43
- api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 || (_api$blockControls = _api$blockControls.commands) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.moveNode(start, end, pos));
38
+ onDrop: function onDrop() {
39
+ var _api$blockControls;
40
+ var _ref2 = (api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.sharedState.currentState()) || {},
41
+ activeNode = _ref2.activeNode,
42
+ decorationState = _ref2.decorationState;
43
+ if (!activeNode || !decorationState) {
44
+ return;
45
+ }
46
+ var _ref3 = decorationState.find(function (dec) {
47
+ return dec.index === index;
48
+ }) || {},
49
+ pos = _ref3.pos;
50
+ if (activeNode && pos !== undefined) {
51
+ var _api$core, _api$blockControls2;
52
+ var start = activeNode.pos;
53
+ 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));
54
+ }
44
55
  }
45
56
  });
46
- }, [pos, api]);
47
- return /*#__PURE__*/_react.default.createElement("div", {
48
- style: {
49
- height: '20px',
50
- marginTop: '-22px',
51
- top: '10px',
52
- position: 'relative',
53
- borderBottom: "solid ".concat(isDraggedOver ? 'blue' : 'transparent', " 2px")
54
- },
55
- ref: ref
56
- });
57
+ }, [index, api]);
58
+ return (
59
+ /*#__PURE__*/
60
+ // Note: Firefox has trouble with using a button element as the handle for drag and drop
61
+ _react.default.createElement("div", {
62
+ style: {
63
+ height: '20px',
64
+ marginTop: '-22px',
65
+ top: '10px',
66
+ position: 'relative',
67
+ borderBottom: "solid ".concat(isDraggedOver ? 'blue' : 'transparent', " 2px")
68
+ },
69
+ ref: ref
70
+ })
71
+ );
57
72
  };
@@ -13,24 +13,30 @@ export const blockControlsPlugin = ({
13
13
  }];
14
14
  },
15
15
  commands: {
16
- moveNode: (start, end, to) => ({
16
+ moveNode: (start, to) => ({
17
17
  tr
18
18
  }) => {
19
- var _api$core;
20
- let node = tr.doc.content.cut(start, end); // cut the content
19
+ var _node$nodeSize, _api$core;
20
+ const node = tr.doc.nodeAt(start);
21
+ const size = (_node$nodeSize = node === null || node === void 0 ? void 0 : node.nodeSize) !== null && _node$nodeSize !== void 0 ? _node$nodeSize : 1;
22
+ const end = start + size;
23
+ let nodeCopy = tr.doc.content.cut(start, end); // cut the content
21
24
  tr.delete(start, end); // delete the content from the original position
22
- tr.insert(tr.mapping.map(to), node); // insert the content at the new position
25
+ const mappedTo = tr.mapping.map(to);
26
+ tr.insert(mappedTo, nodeCopy); // insert the content at the new position
23
27
  api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.focus();
24
28
  return tr;
25
29
  }
26
30
  },
27
31
  getSharedState(editorState) {
28
- var _key$getState$isMenuO, _key$getState;
32
+ var _key$getState$isMenuO, _key$getState, _key$getState$activeN, _key$getState2, _key$getState$decorat, _key$getState3;
29
33
  if (!editorState) {
30
34
  return undefined;
31
35
  }
32
36
  return {
33
- isMenuOpen: (_key$getState$isMenuO = (_key$getState = key.getState(editorState)) === null || _key$getState === void 0 ? void 0 : _key$getState.isMenuOpen) !== null && _key$getState$isMenuO !== void 0 ? _key$getState$isMenuO : false
37
+ isMenuOpen: (_key$getState$isMenuO = (_key$getState = key.getState(editorState)) === null || _key$getState === void 0 ? void 0 : _key$getState.isMenuOpen) !== null && _key$getState$isMenuO !== void 0 ? _key$getState$isMenuO : false,
38
+ activeNode: (_key$getState$activeN = (_key$getState2 = key.getState(editorState)) === null || _key$getState2 === void 0 ? void 0 : _key$getState2.activeNode) !== null && _key$getState$activeN !== void 0 ? _key$getState$activeN : null,
39
+ decorationState: (_key$getState$decorat = (_key$getState3 = key.getState(editorState)) === null || _key$getState3 === void 0 ? void 0 : _key$getState3.decorationState) !== null && _key$getState$decorat !== void 0 ? _key$getState$decorat : []
34
40
  };
35
41
  },
36
42
  contentComponent() {
@@ -4,38 +4,59 @@ import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
4
4
  import { DragHandle } from '../ui/drag-handle';
5
5
  import { DropTarget } from '../ui/drop-target';
6
6
  export const dropTargetDecorations = (oldState, newState, api) => {
7
- const decorations = [];
8
- oldState.doc.nodesBetween(0, newState.doc.nodeSize - 2, (node, pos) => {
9
- decorations.push(Decoration.widget(pos + node.nodeSize, () => {
7
+ const decs = [];
8
+ // Decoration state is used to keep track of the position of the drop targets
9
+ // and allows us to easily map the updated position in the plugin apply method.
10
+ const decorationState = [];
11
+ oldState.doc.nodesBetween(0, newState.doc.nodeSize - 2, (_node, pos, _parent, index) => {
12
+ decorationState.push({
13
+ index,
14
+ pos
15
+ });
16
+ decs.push(Decoration.widget(pos, () => {
10
17
  const element = document.createElement('div');
11
18
  ReactDOM.render( /*#__PURE__*/createElement(DropTarget, {
12
19
  api,
13
- pos: pos + node.nodeSize
20
+ index
14
21
  }), element);
15
22
  return element;
16
23
  }));
17
24
  return false;
18
25
  });
19
- decorations.push(Decoration.widget(0, () => {
26
+
27
+ /**
28
+ * We are adding a drop target at the end of the document because by default we
29
+ * draw all drop targets at the top of every node. It's better to draw the drop targets
30
+ * at the top of each node because that way we only need to know the start position of the
31
+ * node and not its size.
32
+ *
33
+ */
34
+ decorationState.push({
35
+ index: decorationState.length + 1,
36
+ pos: newState.doc.nodeSize - 2
37
+ });
38
+ decs.push(Decoration.widget(newState.doc.nodeSize - 2, () => {
20
39
  const element = document.createElement('div');
21
40
  ReactDOM.render( /*#__PURE__*/createElement(DropTarget, {
22
41
  api,
23
- pos: 0
42
+ index: decorationState.length
24
43
  }), element);
25
44
  return element;
26
45
  }));
27
- return decorations;
46
+ return {
47
+ decs,
48
+ decorationState
49
+ };
28
50
  };
29
51
  export const dragHandleDecoration = (oldState,
30
52
  // @ts-ignore
31
53
  meta, api) => {
32
- return DecorationSet.create(oldState.doc, [Decoration.widget(meta.pos + meta.size, (view, getPos) => {
54
+ return DecorationSet.create(oldState.doc, [Decoration.widget(meta.pos, (view, getPos) => {
33
55
  const element = document.createElement('div');
34
56
  ReactDOM.render( /*#__PURE__*/createElement(DragHandle, {
35
57
  dom: meta.dom,
36
58
  api,
37
- start: meta.pos,
38
- end: meta.pos + meta.size
59
+ start: meta.pos
39
60
  }), element);
40
61
  element.style.position = 'absolute';
41
62
  return element;
@@ -10,19 +10,25 @@ export const createPlugin = api => {
10
10
  init() {
11
11
  return {
12
12
  decorations: DecorationSet.empty,
13
+ decorationState: [],
13
14
  activeNode: null,
14
15
  isDragging: false,
15
- isMenuOpen: false
16
+ isMenuOpen: false,
17
+ start: null,
18
+ end: null
16
19
  };
17
20
  },
18
21
  apply(tr, currentState, oldState, newState) {
19
- var _meta$isDragging;
22
+ var _decorationState, _meta$pos, _meta$isDragging;
23
+ // return currentState;
20
24
  let {
21
25
  activeNode,
22
26
  decorations,
23
- isMenuOpen
27
+ isMenuOpen,
28
+ decorationState
24
29
  } = currentState;
25
30
  const meta = tr.getMeta(key);
31
+
26
32
  // Drag handle decoration
27
33
  if (meta && meta.pos !== (activeNode === null || activeNode === void 0 ? void 0 : activeNode.pos) && api) {
28
34
  decorations = dragHandleDecoration(newState, meta, api);
@@ -30,20 +36,46 @@ export const createPlugin = api => {
30
36
  // Drop target decorations
31
37
  if (meta !== null && meta !== void 0 && meta.isDragging && api) {
32
38
  decorations = DecorationSet.create(newState.doc, []);
33
- const decs = dropTargetDecorations(oldState, newState, api);
39
+ const {
40
+ decs,
41
+ decorationState: updatedDecorationState
42
+ } = dropTargetDecorations(oldState, newState, api);
43
+ decorationState = updatedDecorationState;
34
44
  decorations = decorations.add(newState.doc, decs);
35
45
  }
36
- // Remove target decorations when dragging is stopped
46
+
47
+ // Remove drop target decorations when dragging is stopped
37
48
  if ((meta === null || meta === void 0 ? void 0 : meta.isDragging) === false) {
38
49
  decorations = DecorationSet.create(newState.doc, []);
39
50
  }
51
+
52
+ // Map decorations when the document changes
53
+ if (tr.docChanged && decorations !== DecorationSet.empty) {
54
+ decorations = decorations.map(tr.mapping, tr.doc);
55
+ }
56
+
57
+ // Map drop target decoration positions when the document changes
58
+ if (tr.docChanged && currentState.isDragging) {
59
+ decorationState = decorationState.map(({
60
+ index,
61
+ pos
62
+ }) => {
63
+ return {
64
+ index,
65
+ pos: tr.mapping.map(pos)
66
+ };
67
+ });
68
+ }
69
+
70
+ // Map active node position when the document changes
71
+ const mappedActiveNodePos = tr.docChanged && activeNode ? tr.mapping.map(activeNode.pos) : activeNode === null || activeNode === void 0 ? void 0 : activeNode.pos;
40
72
  return {
41
73
  decorations,
74
+ decorationState: (_decorationState = decorationState) !== null && _decorationState !== void 0 ? _decorationState : currentState.decorationState,
42
75
  activeNode: {
43
- pos: meta === null || meta === void 0 ? void 0 : meta.pos,
44
- size: meta === null || meta === void 0 ? void 0 : meta.size
76
+ pos: (_meta$pos = meta === null || meta === void 0 ? void 0 : meta.pos) !== null && _meta$pos !== void 0 ? _meta$pos : mappedActiveNodePos
45
77
  },
46
- isDragging: (_meta$isDragging = meta === null || meta === void 0 ? void 0 : meta.isDragging) !== null && _meta$isDragging !== void 0 ? _meta$isDragging : false,
78
+ isDragging: (_meta$isDragging = meta === null || meta === void 0 ? void 0 : meta.isDragging) !== null && _meta$isDragging !== void 0 ? _meta$isDragging : currentState.isDragging,
47
79
  isMenuOpen: meta !== null && meta !== void 0 && meta.toggleMenu ? !isMenuOpen : isMenuOpen
48
80
  };
49
81
  }
@@ -79,7 +111,6 @@ export const createPlugin = api => {
79
111
  tr
80
112
  }) => tr.setMeta(key, {
81
113
  pos: topLevelPos,
82
- size: topLevelNode.nodeSize,
83
114
  dom,
84
115
  type: topLevelNode.type.name
85
116
  }));
@@ -1,7 +1,6 @@
1
1
  /** @jsx jsx */
2
2
  import { useEffect, useRef } from 'react';
3
3
  import { css, jsx } from '@emotion/react';
4
- import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
5
4
  import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
6
5
  import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview';
7
6
  import { key } from '../pm-plugins/main';
@@ -9,6 +8,8 @@ import { DRAG_HANDLE_HEIGHT, DRAG_HANDLE_WIDTH } from './consts';
9
8
  import { dragPreview } from './drag-preview';
10
9
  const styles = css({
11
10
  position: 'absolute',
11
+ // eslint-disable-next-line @atlaskit/design-system/ensure-design-token-usage
12
+ background: 'grey',
12
13
  zIndex: 1,
13
14
  height: DRAG_HANDLE_HEIGHT,
14
15
  width: DRAG_HANDLE_WIDTH,
@@ -17,8 +18,7 @@ const styles = css({
17
18
  export const DragHandle = ({
18
19
  dom,
19
20
  api,
20
- start,
21
- end
21
+ start
22
22
  }) => {
23
23
  const buttonRef = useRef(null);
24
24
  const domRef = useRef(dom);
@@ -27,12 +27,8 @@ export const DragHandle = ({
27
27
  if (!element) {
28
28
  return;
29
29
  }
30
- return combine(draggable({
30
+ return draggable({
31
31
  element,
32
- getInitialData: () => ({
33
- start,
34
- end
35
- }),
36
32
  onGenerateDragPreview: ({
37
33
  nativeSetDragImage
38
34
  }) => {
@@ -58,7 +54,8 @@ export const DragHandle = ({
58
54
  api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(({
59
55
  tr
60
56
  }) => tr.setMeta(key, {
61
- isDragging: true
57
+ isDragging: true,
58
+ start
62
59
  }));
63
60
  },
64
61
  onDrop() {
@@ -69,15 +66,14 @@ export const DragHandle = ({
69
66
  isDragging: false
70
67
  }));
71
68
  }
72
- }));
73
- }, [api, start, end]);
74
- return jsx("button", {
69
+ });
70
+ }, [api, start]);
71
+ return jsx("div", {
75
72
  css: styles,
76
73
  style: {
77
- bottom: dom.clientHeight / 2 - DRAG_HANDLE_HEIGHT / 2
74
+ top: dom.clientHeight / 2 - DRAG_HANDLE_HEIGHT / 2
78
75
  },
79
76
  ref: buttonRef,
80
- type: "button",
81
77
  onClick: () => {
82
78
  var _api$core3;
83
79
  api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(({
@@ -2,7 +2,7 @@ import React, { useEffect, useRef, useState } from 'react';
2
2
  import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
3
3
  export const DropTarget = ({
4
4
  api,
5
- pos
5
+ index
6
6
  }) => {
7
7
  const ref = useRef(null);
8
8
  const [isDraggedOver, setIsDraggedOver] = useState(false);
@@ -16,24 +16,40 @@ export const DropTarget = ({
16
16
  getIsSticky: () => true,
17
17
  onDragEnter: () => setIsDraggedOver(true),
18
18
  onDragLeave: () => setIsDraggedOver(false),
19
- onDrop: event => {
20
- var _api$core, _api$blockControls, _api$blockControls$co;
19
+ onDrop: () => {
20
+ var _api$blockControls;
21
21
  const {
22
- start,
23
- end
24
- } = event.source.data;
25
- 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$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : (_api$blockControls$co = _api$blockControls.commands) === null || _api$blockControls$co === void 0 ? void 0 : _api$blockControls$co.moveNode(start, end, pos));
22
+ activeNode,
23
+ decorationState
24
+ } = (api === null || api === void 0 ? void 0 : (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.sharedState.currentState()) || {};
25
+ if (!activeNode || !decorationState) {
26
+ return;
27
+ }
28
+ const {
29
+ pos
30
+ } = decorationState.find(dec => dec.index === index) || {};
31
+ if (activeNode && pos !== undefined) {
32
+ var _api$core, _api$blockControls2, _api$blockControls2$c;
33
+ const {
34
+ pos: start
35
+ } = activeNode;
36
+ 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));
37
+ }
26
38
  }
27
39
  });
28
- }, [pos, api]);
29
- return /*#__PURE__*/React.createElement("div", {
30
- style: {
31
- height: '20px',
32
- marginTop: '-22px',
33
- top: '10px',
34
- position: 'relative',
35
- borderBottom: `solid ${isDraggedOver ? 'blue' : 'transparent'} 2px`
36
- },
37
- ref: ref
38
- });
40
+ }, [index, api]);
41
+ return (
42
+ /*#__PURE__*/
43
+ // Note: Firefox has trouble with using a button element as the handle for drag and drop
44
+ React.createElement("div", {
45
+ style: {
46
+ height: '20px',
47
+ marginTop: '-22px',
48
+ top: '10px',
49
+ position: 'relative',
50
+ borderBottom: `solid ${isDraggedOver ? 'blue' : 'transparent'} 2px`
51
+ },
52
+ ref: ref
53
+ })
54
+ );
39
55
  };
@@ -15,25 +15,31 @@ export var blockControlsPlugin = function blockControlsPlugin(_ref) {
15
15
  }];
16
16
  },
17
17
  commands: {
18
- moveNode: function moveNode(start, end, to) {
18
+ moveNode: function moveNode(start, to) {
19
19
  return function (_ref2) {
20
- var _api$core;
20
+ var _node$nodeSize, _api$core;
21
21
  var tr = _ref2.tr;
22
- var node = tr.doc.content.cut(start, end); // cut the content
22
+ var node = tr.doc.nodeAt(start);
23
+ var size = (_node$nodeSize = node === null || node === void 0 ? void 0 : node.nodeSize) !== null && _node$nodeSize !== void 0 ? _node$nodeSize : 1;
24
+ var end = start + size;
25
+ var nodeCopy = tr.doc.content.cut(start, end); // cut the content
23
26
  tr.delete(start, end); // delete the content from the original position
24
- tr.insert(tr.mapping.map(to), node); // insert the content at the new position
27
+ var mappedTo = tr.mapping.map(to);
28
+ tr.insert(mappedTo, nodeCopy); // insert the content at the new position
25
29
  api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.focus();
26
30
  return tr;
27
31
  };
28
32
  }
29
33
  },
30
34
  getSharedState: function getSharedState(editorState) {
31
- var _key$getState$isMenuO, _key$getState;
35
+ var _key$getState$isMenuO, _key$getState, _key$getState$activeN, _key$getState2, _key$getState$decorat, _key$getState3;
32
36
  if (!editorState) {
33
37
  return undefined;
34
38
  }
35
39
  return {
36
- isMenuOpen: (_key$getState$isMenuO = (_key$getState = key.getState(editorState)) === null || _key$getState === void 0 ? void 0 : _key$getState.isMenuOpen) !== null && _key$getState$isMenuO !== void 0 ? _key$getState$isMenuO : false
40
+ isMenuOpen: (_key$getState$isMenuO = (_key$getState = key.getState(editorState)) === null || _key$getState === void 0 ? void 0 : _key$getState.isMenuOpen) !== null && _key$getState$isMenuO !== void 0 ? _key$getState$isMenuO : false,
41
+ activeNode: (_key$getState$activeN = (_key$getState2 = key.getState(editorState)) === null || _key$getState2 === void 0 ? void 0 : _key$getState2.activeNode) !== null && _key$getState$activeN !== void 0 ? _key$getState$activeN : null,
42
+ decorationState: (_key$getState$decorat = (_key$getState3 = key.getState(editorState)) === null || _key$getState3 === void 0 ? void 0 : _key$getState3.decorationState) !== null && _key$getState$decorat !== void 0 ? _key$getState$decorat : []
37
43
  };
38
44
  },
39
45
  contentComponent: function contentComponent() {
@@ -4,38 +4,59 @@ import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
4
4
  import { DragHandle } from '../ui/drag-handle';
5
5
  import { DropTarget } from '../ui/drop-target';
6
6
  export var dropTargetDecorations = function dropTargetDecorations(oldState, newState, api) {
7
- var decorations = [];
8
- oldState.doc.nodesBetween(0, newState.doc.nodeSize - 2, function (node, pos) {
9
- decorations.push(Decoration.widget(pos + node.nodeSize, function () {
7
+ var decs = [];
8
+ // Decoration state is used to keep track of the position of the drop targets
9
+ // and allows us to easily map the updated position in the plugin apply method.
10
+ var decorationState = [];
11
+ oldState.doc.nodesBetween(0, newState.doc.nodeSize - 2, function (_node, pos, _parent, index) {
12
+ decorationState.push({
13
+ index: index,
14
+ pos: pos
15
+ });
16
+ decs.push(Decoration.widget(pos, function () {
10
17
  var element = document.createElement('div');
11
18
  ReactDOM.render( /*#__PURE__*/createElement(DropTarget, {
12
19
  api: api,
13
- pos: pos + node.nodeSize
20
+ index: index
14
21
  }), element);
15
22
  return element;
16
23
  }));
17
24
  return false;
18
25
  });
19
- decorations.push(Decoration.widget(0, function () {
26
+
27
+ /**
28
+ * We are adding a drop target at the end of the document because by default we
29
+ * draw all drop targets at the top of every node. It's better to draw the drop targets
30
+ * at the top of each node because that way we only need to know the start position of the
31
+ * node and not its size.
32
+ *
33
+ */
34
+ decorationState.push({
35
+ index: decorationState.length + 1,
36
+ pos: newState.doc.nodeSize - 2
37
+ });
38
+ decs.push(Decoration.widget(newState.doc.nodeSize - 2, function () {
20
39
  var element = document.createElement('div');
21
40
  ReactDOM.render( /*#__PURE__*/createElement(DropTarget, {
22
41
  api: api,
23
- pos: 0
42
+ index: decorationState.length
24
43
  }), element);
25
44
  return element;
26
45
  }));
27
- return decorations;
46
+ return {
47
+ decs: decs,
48
+ decorationState: decorationState
49
+ };
28
50
  };
29
51
  export var dragHandleDecoration = function dragHandleDecoration(oldState,
30
52
  // @ts-ignore
31
53
  meta, api) {
32
- return DecorationSet.create(oldState.doc, [Decoration.widget(meta.pos + meta.size, function (view, getPos) {
54
+ return DecorationSet.create(oldState.doc, [Decoration.widget(meta.pos, function (view, getPos) {
33
55
  var element = document.createElement('div');
34
56
  ReactDOM.render( /*#__PURE__*/createElement(DragHandle, {
35
57
  dom: meta.dom,
36
58
  api: api,
37
- start: meta.pos,
38
- end: meta.pos + meta.size
59
+ start: meta.pos
39
60
  }), element);
40
61
  element.style.position = 'absolute';
41
62
  return element;
@@ -10,17 +10,23 @@ export var createPlugin = function createPlugin(api) {
10
10
  init: function init() {
11
11
  return {
12
12
  decorations: DecorationSet.empty,
13
+ decorationState: [],
13
14
  activeNode: null,
14
15
  isDragging: false,
15
- isMenuOpen: false
16
+ isMenuOpen: false,
17
+ start: null,
18
+ end: null
16
19
  };
17
20
  },
18
21
  apply: function apply(tr, currentState, oldState, newState) {
19
- var _meta$isDragging;
22
+ var _decorationState, _meta$pos, _meta$isDragging;
23
+ // return currentState;
20
24
  var activeNode = currentState.activeNode,
21
25
  decorations = currentState.decorations,
22
- isMenuOpen = currentState.isMenuOpen;
26
+ isMenuOpen = currentState.isMenuOpen,
27
+ decorationState = currentState.decorationState;
23
28
  var meta = tr.getMeta(key);
29
+
24
30
  // Drag handle decoration
25
31
  if (meta && meta.pos !== (activeNode === null || activeNode === void 0 ? void 0 : activeNode.pos) && api) {
26
32
  decorations = dragHandleDecoration(newState, meta, api);
@@ -28,20 +34,44 @@ export var createPlugin = function createPlugin(api) {
28
34
  // Drop target decorations
29
35
  if (meta !== null && meta !== void 0 && meta.isDragging && api) {
30
36
  decorations = DecorationSet.create(newState.doc, []);
31
- var decs = dropTargetDecorations(oldState, newState, api);
37
+ var _dropTargetDecoration = dropTargetDecorations(oldState, newState, api),
38
+ decs = _dropTargetDecoration.decs,
39
+ updatedDecorationState = _dropTargetDecoration.decorationState;
40
+ decorationState = updatedDecorationState;
32
41
  decorations = decorations.add(newState.doc, decs);
33
42
  }
34
- // Remove target decorations when dragging is stopped
43
+
44
+ // Remove drop target decorations when dragging is stopped
35
45
  if ((meta === null || meta === void 0 ? void 0 : meta.isDragging) === false) {
36
46
  decorations = DecorationSet.create(newState.doc, []);
37
47
  }
48
+
49
+ // Map decorations when the document changes
50
+ if (tr.docChanged && decorations !== DecorationSet.empty) {
51
+ decorations = decorations.map(tr.mapping, tr.doc);
52
+ }
53
+
54
+ // Map drop target decoration positions when the document changes
55
+ if (tr.docChanged && currentState.isDragging) {
56
+ decorationState = decorationState.map(function (_ref) {
57
+ var index = _ref.index,
58
+ pos = _ref.pos;
59
+ return {
60
+ index: index,
61
+ pos: tr.mapping.map(pos)
62
+ };
63
+ });
64
+ }
65
+
66
+ // Map active node position when the document changes
67
+ var mappedActiveNodePos = tr.docChanged && activeNode ? tr.mapping.map(activeNode.pos) : activeNode === null || activeNode === void 0 ? void 0 : activeNode.pos;
38
68
  return {
39
69
  decorations: decorations,
70
+ decorationState: (_decorationState = decorationState) !== null && _decorationState !== void 0 ? _decorationState : currentState.decorationState,
40
71
  activeNode: {
41
- pos: meta === null || meta === void 0 ? void 0 : meta.pos,
42
- size: meta === null || meta === void 0 ? void 0 : meta.size
72
+ pos: (_meta$pos = meta === null || meta === void 0 ? void 0 : meta.pos) !== null && _meta$pos !== void 0 ? _meta$pos : mappedActiveNodePos
43
73
  },
44
- isDragging: (_meta$isDragging = meta === null || meta === void 0 ? void 0 : meta.isDragging) !== null && _meta$isDragging !== void 0 ? _meta$isDragging : false,
74
+ isDragging: (_meta$isDragging = meta === null || meta === void 0 ? void 0 : meta.isDragging) !== null && _meta$isDragging !== void 0 ? _meta$isDragging : currentState.isDragging,
45
75
  isMenuOpen: meta !== null && meta !== void 0 && meta.toggleMenu ? !isMenuOpen : isMenuOpen
46
76
  };
47
77
  }
@@ -73,11 +103,10 @@ export var createPlugin = function createPlugin(api) {
73
103
  if (!dom) {
74
104
  return;
75
105
  }
76
- api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref) {
77
- var tr = _ref.tr;
106
+ api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref2) {
107
+ var tr = _ref2.tr;
78
108
  return tr.setMeta(key, {
79
109
  pos: topLevelPos,
80
- size: topLevelNode.nodeSize,
81
110
  dom: dom,
82
111
  type: topLevelNode.type.name
83
112
  });
@@ -1,7 +1,6 @@
1
1
  /** @jsx jsx */
2
2
  import { useEffect, useRef } from 'react';
3
3
  import { css, jsx } from '@emotion/react';
4
- import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
5
4
  import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
6
5
  import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview';
7
6
  import { key } from '../pm-plugins/main';
@@ -9,6 +8,8 @@ import { DRAG_HANDLE_HEIGHT, DRAG_HANDLE_WIDTH } from './consts';
9
8
  import { dragPreview } from './drag-preview';
10
9
  var styles = css({
11
10
  position: 'absolute',
11
+ // eslint-disable-next-line @atlaskit/design-system/ensure-design-token-usage
12
+ background: 'grey',
12
13
  zIndex: 1,
13
14
  height: DRAG_HANDLE_HEIGHT,
14
15
  width: DRAG_HANDLE_WIDTH,
@@ -17,8 +18,7 @@ var styles = css({
17
18
  export var DragHandle = function DragHandle(_ref) {
18
19
  var dom = _ref.dom,
19
20
  api = _ref.api,
20
- start = _ref.start,
21
- end = _ref.end;
21
+ start = _ref.start;
22
22
  var buttonRef = useRef(null);
23
23
  var domRef = useRef(dom);
24
24
  useEffect(function () {
@@ -26,14 +26,8 @@ export var DragHandle = function DragHandle(_ref) {
26
26
  if (!element) {
27
27
  return;
28
28
  }
29
- return combine(draggable({
29
+ return draggable({
30
30
  element: element,
31
- getInitialData: function getInitialData() {
32
- return {
33
- start: start,
34
- end: end
35
- };
36
- },
37
31
  onGenerateDragPreview: function onGenerateDragPreview(_ref2) {
38
32
  var nativeSetDragImage = _ref2.nativeSetDragImage;
39
33
  setCustomNativeDragPreview({
@@ -57,7 +51,8 @@ export var DragHandle = function DragHandle(_ref) {
57
51
  api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref4) {
58
52
  var tr = _ref4.tr;
59
53
  return tr.setMeta(key, {
60
- isDragging: true
54
+ isDragging: true,
55
+ start: start
61
56
  });
62
57
  });
63
58
  },
@@ -70,15 +65,14 @@ export var DragHandle = function DragHandle(_ref) {
70
65
  });
71
66
  });
72
67
  }
73
- }));
74
- }, [api, start, end]);
75
- return jsx("button", {
68
+ });
69
+ }, [api, start]);
70
+ return jsx("div", {
76
71
  css: styles,
77
72
  style: {
78
- bottom: dom.clientHeight / 2 - DRAG_HANDLE_HEIGHT / 2
73
+ top: dom.clientHeight / 2 - DRAG_HANDLE_HEIGHT / 2
79
74
  },
80
75
  ref: buttonRef,
81
- type: "button",
82
76
  onClick: function onClick() {
83
77
  var _api$core3;
84
78
  api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(function (_ref6) {
@@ -3,7 +3,7 @@ import React, { useEffect, useRef, useState } from 'react';
3
3
  import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
4
4
  export var DropTarget = function DropTarget(_ref) {
5
5
  var api = _ref.api,
6
- pos = _ref.pos;
6
+ index = _ref.index;
7
7
  var ref = useRef(null);
8
8
  var _useState = useState(false),
9
9
  _useState2 = _slicedToArray(_useState, 2),
@@ -25,23 +25,38 @@ export var DropTarget = function DropTarget(_ref) {
25
25
  onDragLeave: function onDragLeave() {
26
26
  return setIsDraggedOver(false);
27
27
  },
28
- onDrop: function onDrop(event) {
29
- var _api$core, _api$blockControls;
30
- var _ref2 = event.source.data,
31
- start = _ref2.start,
32
- end = _ref2.end;
33
- api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 || (_api$blockControls = _api$blockControls.commands) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.moveNode(start, end, pos));
28
+ onDrop: function onDrop() {
29
+ var _api$blockControls;
30
+ var _ref2 = (api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.sharedState.currentState()) || {},
31
+ activeNode = _ref2.activeNode,
32
+ decorationState = _ref2.decorationState;
33
+ if (!activeNode || !decorationState) {
34
+ return;
35
+ }
36
+ var _ref3 = decorationState.find(function (dec) {
37
+ return dec.index === index;
38
+ }) || {},
39
+ pos = _ref3.pos;
40
+ if (activeNode && pos !== undefined) {
41
+ var _api$core, _api$blockControls2;
42
+ var start = activeNode.pos;
43
+ 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));
44
+ }
34
45
  }
35
46
  });
36
- }, [pos, api]);
37
- return /*#__PURE__*/React.createElement("div", {
38
- style: {
39
- height: '20px',
40
- marginTop: '-22px',
41
- top: '10px',
42
- position: 'relative',
43
- borderBottom: "solid ".concat(isDraggedOver ? 'blue' : 'transparent', " 2px")
44
- },
45
- ref: ref
46
- });
47
+ }, [index, api]);
48
+ return (
49
+ /*#__PURE__*/
50
+ // Note: Firefox has trouble with using a button element as the handle for drag and drop
51
+ React.createElement("div", {
52
+ style: {
53
+ height: '20px',
54
+ marginTop: '-22px',
55
+ top: '10px',
56
+ position: 'relative',
57
+ borderBottom: "solid ".concat(isDraggedOver ? 'blue' : 'transparent', " 2px")
58
+ },
59
+ ref: ref
60
+ })
61
+ );
47
62
  };
@@ -2,5 +2,11 @@ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
2
2
  import type { EditorState } from '@atlaskit/editor-prosemirror/state';
3
3
  import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
4
4
  import type { BlockControlsPlugin } from '../types';
5
- export declare const dropTargetDecorations: (oldState: EditorState, newState: EditorState, api: ExtractInjectionAPI<BlockControlsPlugin>) => Decoration[];
5
+ export declare const dropTargetDecorations: (oldState: EditorState, newState: EditorState, api: ExtractInjectionAPI<BlockControlsPlugin>) => {
6
+ decs: Decoration[];
7
+ decorationState: {
8
+ index: number;
9
+ pos: number;
10
+ }[];
11
+ };
6
12
  export declare const dragHandleDecoration: (oldState: EditorState, meta: any, api: ExtractInjectionAPI<BlockControlsPlugin>) => DecorationSet;
@@ -1,16 +1,6 @@
1
1
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
2
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
3
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
4
- import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
5
- import type { BlockControlsPlugin } from '../types';
4
+ import type { BlockControlsPlugin, PluginState } from '../types';
6
5
  export declare const key: PluginKey<PluginState>;
7
- export interface PluginState {
8
- decorations: DecorationSet;
9
- isDragging: boolean;
10
- isMenuOpen?: boolean;
11
- activeNode: {
12
- pos: number;
13
- size: number;
14
- } | null;
15
- }
16
6
  export declare const createPlugin: (api: ExtractInjectionAPI<BlockControlsPlugin> | undefined) => SafePlugin<PluginState>;
@@ -1,16 +1,30 @@
1
1
  import type { EditorCommand, NextEditorPlugin } from '@atlaskit/editor-common/types';
2
+ import { type DecorationSet } from '@atlaskit/editor-prosemirror/view';
3
+ export interface PluginState {
4
+ decorations: DecorationSet;
5
+ decorationState: DecorationState;
6
+ isDragging: boolean;
7
+ isMenuOpen?: boolean;
8
+ activeNode: {
9
+ pos: number;
10
+ } | null;
11
+ }
2
12
  export type ReleaseHiddenDecoration = () => boolean | undefined;
3
13
  export type BlockControlsPlugin = NextEditorPlugin<'blockControls', {
4
14
  dependencies: [];
5
15
  sharedState: {
6
16
  isMenuOpen: boolean;
17
+ activeNode: {
18
+ pos: number;
19
+ };
20
+ decorationState: DecorationState;
7
21
  } | undefined;
8
22
  actions: {};
9
23
  commands: {
10
- moveNode: (start: number, end: number, to: number) => EditorCommand;
24
+ moveNode: (start: number, to: number) => EditorCommand;
11
25
  };
12
26
  }>;
13
- export interface DraggableSourceData extends Record<string | symbol, unknown> {
14
- start: number;
15
- end: number;
16
- }
27
+ export type DecorationState = {
28
+ index: number;
29
+ pos: number;
30
+ }[];
@@ -1,9 +1,8 @@
1
1
  import { jsx } from '@emotion/react';
2
2
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
3
  import type { BlockControlsPlugin } from '../types';
4
- export declare const DragHandle: ({ dom, api, start, end, }: {
4
+ export declare const DragHandle: ({ dom, api, start, }: {
5
5
  dom: HTMLElement;
6
6
  api: ExtractInjectionAPI<BlockControlsPlugin> | undefined;
7
7
  start: number;
8
- end: number;
9
8
  }) => jsx.JSX.Element;
@@ -1,7 +1,7 @@
1
1
  /// <reference types="react" />
2
2
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
3
  import type { BlockControlsPlugin } from '../types';
4
- export declare const DropTarget: ({ api, pos, }: {
4
+ export declare const DropTarget: ({ api, index, }: {
5
5
  api: ExtractInjectionAPI<BlockControlsPlugin> | undefined;
6
- pos: number;
6
+ index: number;
7
7
  }) => JSX.Element;
@@ -2,5 +2,11 @@ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
2
2
  import type { EditorState } from '@atlaskit/editor-prosemirror/state';
3
3
  import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
4
4
  import type { BlockControlsPlugin } from '../types';
5
- export declare const dropTargetDecorations: (oldState: EditorState, newState: EditorState, api: ExtractInjectionAPI<BlockControlsPlugin>) => Decoration[];
5
+ export declare const dropTargetDecorations: (oldState: EditorState, newState: EditorState, api: ExtractInjectionAPI<BlockControlsPlugin>) => {
6
+ decs: Decoration[];
7
+ decorationState: {
8
+ index: number;
9
+ pos: number;
10
+ }[];
11
+ };
6
12
  export declare const dragHandleDecoration: (oldState: EditorState, meta: any, api: ExtractInjectionAPI<BlockControlsPlugin>) => DecorationSet;
@@ -1,16 +1,6 @@
1
1
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
2
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
3
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
4
- import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
5
- import type { BlockControlsPlugin } from '../types';
4
+ import type { BlockControlsPlugin, PluginState } from '../types';
6
5
  export declare const key: PluginKey<PluginState>;
7
- export interface PluginState {
8
- decorations: DecorationSet;
9
- isDragging: boolean;
10
- isMenuOpen?: boolean;
11
- activeNode: {
12
- pos: number;
13
- size: number;
14
- } | null;
15
- }
16
6
  export declare const createPlugin: (api: ExtractInjectionAPI<BlockControlsPlugin> | undefined) => SafePlugin<PluginState>;
@@ -1,17 +1,31 @@
1
1
  import type { EditorCommand, NextEditorPlugin } from '@atlaskit/editor-common/types';
2
+ import { type DecorationSet } from '@atlaskit/editor-prosemirror/view';
3
+ export interface PluginState {
4
+ decorations: DecorationSet;
5
+ decorationState: DecorationState;
6
+ isDragging: boolean;
7
+ isMenuOpen?: boolean;
8
+ activeNode: {
9
+ pos: number;
10
+ } | null;
11
+ }
2
12
  export type ReleaseHiddenDecoration = () => boolean | undefined;
3
13
  export type BlockControlsPlugin = NextEditorPlugin<'blockControls', {
4
14
  dependencies: [
5
15
  ];
6
16
  sharedState: {
7
17
  isMenuOpen: boolean;
18
+ activeNode: {
19
+ pos: number;
20
+ };
21
+ decorationState: DecorationState;
8
22
  } | undefined;
9
23
  actions: {};
10
24
  commands: {
11
- moveNode: (start: number, end: number, to: number) => EditorCommand;
25
+ moveNode: (start: number, to: number) => EditorCommand;
12
26
  };
13
27
  }>;
14
- export interface DraggableSourceData extends Record<string | symbol, unknown> {
15
- start: number;
16
- end: number;
17
- }
28
+ export type DecorationState = {
29
+ index: number;
30
+ pos: number;
31
+ }[];
@@ -1,9 +1,8 @@
1
1
  import { jsx } from '@emotion/react';
2
2
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
3
  import type { BlockControlsPlugin } from '../types';
4
- export declare const DragHandle: ({ dom, api, start, end, }: {
4
+ export declare const DragHandle: ({ dom, api, start, }: {
5
5
  dom: HTMLElement;
6
6
  api: ExtractInjectionAPI<BlockControlsPlugin> | undefined;
7
7
  start: number;
8
- end: number;
9
8
  }) => jsx.JSX.Element;
@@ -1,7 +1,7 @@
1
1
  /// <reference types="react" />
2
2
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
3
  import type { BlockControlsPlugin } from '../types';
4
- export declare const DropTarget: ({ api, pos, }: {
4
+ export declare const DropTarget: ({ api, index, }: {
5
5
  api: ExtractInjectionAPI<BlockControlsPlugin> | undefined;
6
- pos: number;
6
+ index: number;
7
7
  }) => JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-block-controls",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Block controls plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -31,7 +31,7 @@
31
31
  ".": "./src/index.ts"
32
32
  },
33
33
  "dependencies": {
34
- "@atlaskit/editor-common": "^78.33.0",
34
+ "@atlaskit/editor-common": "^78.37.0",
35
35
  "@atlaskit/editor-prosemirror": "4.0.0",
36
36
  "@atlaskit/pragmatic-drag-and-drop": "^1.1.0",
37
37
  "@babel/runtime": "^7.0.0",