@atlaskit/editor-plugin-block-controls 2.2.1 → 2.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.
Files changed (36) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/cjs/commands/move-node.js +2 -1
  3. package/dist/cjs/pm-plugins/decorations.js +127 -43
  4. package/dist/cjs/pm-plugins/main.js +8 -1
  5. package/dist/cjs/ui/consts.js +3 -3
  6. package/dist/cjs/ui/drop-target-v2.js +184 -0
  7. package/dist/cjs/utils/anchor-utils.js +70 -0
  8. package/dist/cjs/utils/getSelection.js +43 -16
  9. package/dist/cjs/utils/index.js +12 -0
  10. package/dist/es2019/commands/move-node.js +2 -1
  11. package/dist/es2019/pm-plugins/decorations.js +122 -33
  12. package/dist/es2019/pm-plugins/main.js +8 -1
  13. package/dist/es2019/ui/consts.js +3 -3
  14. package/dist/es2019/ui/drop-target-v2.js +171 -0
  15. package/dist/es2019/utils/anchor-utils.js +51 -0
  16. package/dist/es2019/utils/getSelection.js +44 -15
  17. package/dist/es2019/utils/index.js +1 -1
  18. package/dist/esm/commands/move-node.js +2 -1
  19. package/dist/esm/pm-plugins/decorations.js +126 -42
  20. package/dist/esm/pm-plugins/main.js +8 -1
  21. package/dist/esm/ui/consts.js +3 -3
  22. package/dist/esm/ui/drop-target-v2.js +176 -0
  23. package/dist/esm/utils/anchor-utils.js +63 -0
  24. package/dist/esm/utils/getSelection.js +42 -15
  25. package/dist/esm/utils/index.js +1 -1
  26. package/dist/types/pm-plugins/decorations.d.ts +6 -1
  27. package/dist/types/ui/drop-target-v2.d.ts +8 -0
  28. package/dist/types/utils/anchor-utils.d.ts +12 -0
  29. package/dist/types/utils/getSelection.d.ts +5 -0
  30. package/dist/types/utils/index.d.ts +1 -1
  31. package/dist/types-ts4.5/pm-plugins/decorations.d.ts +6 -1
  32. package/dist/types-ts4.5/ui/drop-target-v2.d.ts +8 -0
  33. package/dist/types-ts4.5/utils/anchor-utils.d.ts +12 -0
  34. package/dist/types-ts4.5/utils/getSelection.d.ts +5 -0
  35. package/dist/types-ts4.5/utils/index.d.ts +1 -1
  36. package/package.json +6 -3
@@ -3,9 +3,32 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.selectNode = exports.getSelection = void 0;
6
+ exports.setCursorPositionAtMovedNode = exports.selectNode = exports.getSelection = exports.getInlineNodePos = void 0;
7
+ var _selection = require("@atlaskit/editor-common/selection");
7
8
  var _state = require("@atlaskit/editor-prosemirror/state");
8
9
  var _utils = require("@atlaskit/editor-tables/utils");
10
+ var getInlineNodePos = exports.getInlineNodePos = function getInlineNodePos(tr, start, nodeSize) {
11
+ var $startPos = tr.doc.resolve(start);
12
+ // To trigger the annotation floating toolbar for non-selectable node, we need to select inline nodes
13
+ // Find the first inline node in the node
14
+ var inlineNodePos = start;
15
+ var foundInlineNode = false;
16
+ var inlineNodeEndPos = 0;
17
+ tr.doc.nodesBetween($startPos.pos, $startPos.pos + nodeSize, function (n, pos) {
18
+ if (n.isInline) {
19
+ inlineNodeEndPos = pos + n.nodeSize;
20
+ }
21
+ if (n.isInline && !foundInlineNode) {
22
+ inlineNodePos = pos;
23
+ foundInlineNode = true;
24
+ }
25
+ return true;
26
+ });
27
+ return {
28
+ inlineNodePos: inlineNodePos,
29
+ inlineNodeEndPos: inlineNodeEndPos
30
+ };
31
+ };
9
32
  var getSelection = exports.getSelection = function getSelection(tr, start) {
10
33
  var node = tr.doc.nodeAt(start);
11
34
  var isNodeSelection = node && _state.NodeSelection.isSelectable(node);
@@ -25,21 +48,9 @@ var getSelection = exports.getSelection = function getSelection(tr, start) {
25
48
  nodeName === 'mediaGroup') {
26
49
  return new _state.NodeSelection($startPos);
27
50
  } else {
28
- // To trigger the annotation floating toolbar for non-selectable node, we need to select inline nodes
29
- // Find the first inline node in the node
30
- var inlineNodePos = start;
31
- var foundInlineNode = false;
32
- var inlineNodeEndPos = 0;
33
- tr.doc.nodesBetween($startPos.pos, $startPos.pos + nodeSize, function (n, pos) {
34
- if (n.isInline) {
35
- inlineNodeEndPos = pos + n.nodeSize;
36
- }
37
- if (n.isInline && !foundInlineNode) {
38
- inlineNodePos = pos;
39
- foundInlineNode = true;
40
- }
41
- return true;
42
- });
51
+ var _getInlineNodePos = getInlineNodePos(tr, start, nodeSize),
52
+ inlineNodePos = _getInlineNodePos.inlineNodePos,
53
+ inlineNodeEndPos = _getInlineNodePos.inlineNodeEndPos;
43
54
  return new _state.TextSelection(tr.doc.resolve(inlineNodeEndPos), tr.doc.resolve(inlineNodePos));
44
55
  }
45
56
  };
@@ -51,4 +62,20 @@ var selectNode = exports.selectNode = function selectNode(tr, start, nodeType) {
51
62
  tr.setSelection(getSelection(tr, start));
52
63
  }
53
64
  return tr;
65
+ };
66
+ var setCursorPositionAtMovedNode = exports.setCursorPositionAtMovedNode = function setCursorPositionAtMovedNode(tr, start) {
67
+ var node = tr.doc.nodeAt(start);
68
+ var isNodeSelection = node && _state.NodeSelection.isSelectable(node);
69
+ var nodeSize = node ? node.nodeSize : 1;
70
+ var selection;
71
+ // decisionList node is not selectable, but we want to select the whole node not just text
72
+ if (isNodeSelection || (node === null || node === void 0 ? void 0 : node.type.name) === 'decisionList') {
73
+ selection = new _selection.GapCursorSelection(tr.doc.resolve(start + node.nodeSize), _selection.Side.RIGHT);
74
+ } else {
75
+ var _getInlineNodePos2 = getInlineNodePos(tr, start, nodeSize),
76
+ inlineNodeEndPos = _getInlineNodePos2.inlineNodeEndPos;
77
+ selection = new _state.TextSelection(tr.doc.resolve(inlineNodeEndPos));
78
+ }
79
+ tr.setSelection(selection);
80
+ return tr;
54
81
  };
@@ -3,6 +3,12 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ Object.defineProperty(exports, "getInlineNodePos", {
7
+ enumerable: true,
8
+ get: function get() {
9
+ return _getSelection.getInlineNodePos;
10
+ }
11
+ });
6
12
  Object.defineProperty(exports, "getNestedNodePosition", {
7
13
  enumerable: true,
8
14
  get: function get() {
@@ -21,5 +27,11 @@ Object.defineProperty(exports, "selectNode", {
21
27
  return _getSelection.selectNode;
22
28
  }
23
29
  });
30
+ Object.defineProperty(exports, "setCursorPositionAtMovedNode", {
31
+ enumerable: true,
32
+ get: function get() {
33
+ return _getSelection.setCursorPositionAtMovedNode;
34
+ }
35
+ });
24
36
  var _getSelection = require("./getSelection");
25
37
  var _getNestedNodePosition = require("./getNestedNodePosition");
@@ -10,6 +10,7 @@ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
10
10
  import { DIRECTION } from '../consts';
11
11
  import { key } from '../pm-plugins/main';
12
12
  import { getNestedNodePosition, selectNode } from '../utils';
13
+ import { setCursorPositionAtMovedNode } from '../utils/getSelection';
13
14
  import { canMoveNodeToIndex, isInsideTable, transformSliceExpandToNestedExpand } from '../utils/validation';
14
15
 
15
16
  /**
@@ -183,7 +184,7 @@ export const moveNode = api => (start, to, inputMethod = INPUT_METHOD.DRAG_AND_D
183
184
  mappedTo = tr.mapping.map(to);
184
185
  tr.insert(mappedTo, nodeCopy); // insert the content at the new position
185
186
  }
186
- tr = selectNode(tr, mappedTo, node.type.name);
187
+ tr = inputMethod === INPUT_METHOD.DRAG_AND_DROP && fg('platform_editor_element_dnd_nested_fix_patch_2') ? setCursorPositionAtMovedNode(tr, mappedTo) : selectNode(tr, mappedTo, node.type.name);
187
188
  tr.setMeta(key, {
188
189
  nodeMoved: true
189
190
  });
@@ -8,8 +8,10 @@ import { isEmptyParagraph } from '@atlaskit/editor-common/utils';
8
8
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
9
9
  import { fg } from '@atlaskit/platform-feature-flags';
10
10
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
11
+ import { nodeMargins } from '../ui/consts';
11
12
  import { DragHandle } from '../ui/drag-handle';
12
13
  import { DropTarget } from '../ui/drop-target';
14
+ import { DropTargetV2, EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_GAP, EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_OFFSET } from '../ui/drop-target-v2';
13
15
  import { isBlocksDragTargetDebug } from '../utils/drag-target-debug';
14
16
  import { canMoveNodeToIndex } from '../utils/validation';
15
17
  const IGNORE_NODES = ['tableCell', 'tableHeader', 'tableRow', 'layoutColumn', 'listItem', 'caption'];
@@ -17,24 +19,104 @@ const IGNORE_NODE_DESCENDANTS = ['listItem', 'taskList', 'decisionList', 'mediaS
17
19
  const PARENT_WITH_END_DROP_TARGET = ['tableCell', 'tableHeader', 'panel', 'layoutColumn', 'expand', 'nestedExpand', 'bodiedExtension'];
18
20
  const DISABLE_CHILD_DROP_TARGET = ['orderedList', 'bulletList'];
19
21
  const getNestedDepth = () => editorExperiment('nested-dnd', true) ? 100 : 0;
20
- const createDropTargetDecoration = (pos, dropTargetDec) => {
22
+ export const getNodeAnchor = node => {
23
+ const handleId = ObjHash.getForNode(node);
24
+ return `--node-anchor-${node.type.name}-${handleId}`;
25
+ };
26
+ const getNodeMargins = node => {
27
+ if (!node) {
28
+ return nodeMargins['default'];
29
+ }
30
+ const nodeTypeName = node.type.name;
31
+ if (nodeTypeName === 'heading') {
32
+ return nodeMargins[`heading${node.attrs.level}`] || nodeMargins['default'];
33
+ }
34
+ return nodeMargins[nodeTypeName] || nodeMargins['default'];
35
+ };
36
+ const getGapAndOffset = (prevNode, nextNode, parentNode) => {
37
+ if (!prevNode && nextNode) {
38
+ // first node
39
+ return {
40
+ gap: 0,
41
+ offset: 0
42
+ };
43
+ } else if (prevNode && !nextNode) {
44
+ return {
45
+ gap: 0,
46
+ offset: 0
47
+ };
48
+ }
49
+ const top = getNodeMargins(nextNode).top || 4;
50
+ const bottom = getNodeMargins(prevNode).bottom || 4;
51
+ const gap = Math.max(top, bottom);
52
+ let offset = top - gap / 2;
53
+ if ((prevNode === null || prevNode === void 0 ? void 0 : prevNode.type.name) === 'mediaSingle' && (nextNode === null || nextNode === void 0 ? void 0 : nextNode.type.name) === 'mediaSingle') {
54
+ offset = -offset;
55
+ } else if (prevNode !== null && prevNode !== void 0 && prevNode.type.name && ['tableCell', 'tableHeader'].includes(prevNode === null || prevNode === void 0 ? void 0 : prevNode.type.name)) {
56
+ offset = 0;
57
+ }
58
+ return {
59
+ gap,
60
+ offset
61
+ };
62
+ };
63
+ const shouldDescend = node => {
64
+ if (fg('platform_editor_drag_and_drop_target_v2')) {
65
+ return !['mediaSingle', 'paragraph', 'heading'].includes(node.type.name);
66
+ }
67
+ return true;
68
+ };
69
+ export const createDropTargetDecoration = (pos, props, side, anchorHeightsCache) => {
21
70
  return Decoration.widget(pos, (_, getPos) => {
22
71
  const element = document.createElement('div');
23
72
  element.setAttribute('data-blocks-drop-target-container', 'true');
24
73
  element.style.clear = 'unset';
25
- ReactDOM.render(dropTargetDec(getPos), element);
74
+ if (fg('platform_editor_drag_and_drop_target_v2')) {
75
+ const {
76
+ gap,
77
+ offset
78
+ } = getGapAndOffset(props.prevNode, props.nextNode, props.parentNode);
79
+ element.style.setProperty(EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_OFFSET, `${offset}px`);
80
+ element.style.setProperty(EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_GAP, `${gap}px`);
81
+ }
82
+ if (fg('platform_editor_drag_and_drop_target_v2')) {
83
+ ReactDOM.render( /*#__PURE__*/createElement(DropTargetV2, {
84
+ ...props,
85
+ getPos,
86
+ anchorHeightsCache
87
+ }), element);
88
+ } else {
89
+ ReactDOM.render( /*#__PURE__*/createElement(DropTarget, {
90
+ ...props,
91
+ getPos
92
+ }), element);
93
+ }
26
94
  return element;
27
95
  }, {
28
96
  type: 'drop-target-decoration',
29
- side: -1
97
+ side
30
98
  });
31
99
  };
32
- export const dropTargetDecorations = (newState, api, formatMessage, activeNode) => {
100
+ export const dropTargetDecorations = (newState, api, formatMessage, activeNode, anchorHeightsCache) => {
33
101
  const decs = [];
34
102
  unmountDecorations('data-blocks-drop-target-container');
35
103
  let prevNode;
36
104
  const activeNodePos = activeNode === null || activeNode === void 0 ? void 0 : activeNode.pos;
37
105
  const activePMNode = typeof activeNodePos === 'number' && newState.doc.resolve(activeNodePos).nodeAfter;
106
+ anchorHeightsCache === null || anchorHeightsCache === void 0 ? void 0 : anchorHeightsCache.clear();
107
+ const prevNodeStack = [];
108
+ const popNodeStack = depth => {
109
+ let result;
110
+ const toDepth = Math.max(depth, 0);
111
+ while (prevNodeStack.length > toDepth) {
112
+ result = prevNodeStack.pop();
113
+ }
114
+ return result;
115
+ };
116
+ const pushNodeStack = (node, depth) => {
117
+ popNodeStack(depth);
118
+ prevNodeStack.push(node);
119
+ };
38
120
  newState.doc.descendants((node, pos, parent, index) => {
39
121
  let depth = 0;
40
122
  // drop target deco at the end position
@@ -42,58 +124,65 @@ export const dropTargetDecorations = (newState, api, formatMessage, activeNode)
42
124
  if (editorExperiment('nested-dnd', true)) {
43
125
  depth = newState.doc.resolve(pos).depth;
44
126
  if (node.isInline || !parent || DISABLE_CHILD_DROP_TARGET.includes(parent.type.name)) {
45
- prevNode = node;
127
+ if (fg('platform_editor_drag_and_drop_target_v2')) {
128
+ pushNodeStack(node, depth);
129
+ } else {
130
+ prevNode = node;
131
+ }
46
132
  return false;
47
133
  }
48
134
  if (IGNORE_NODES.includes(node.type.name)) {
49
- prevNode = node;
50
- return true; //skip over, don't consider it a valid depth
135
+ if (fg('platform_editor_drag_and_drop_target_v2')) {
136
+ pushNodeStack(node, depth);
137
+ } else {
138
+ prevNode = node;
139
+ }
140
+ return shouldDescend(node); //skip over, don't consider it a valid depth
51
141
  }
52
142
  const canDrop = activePMNode && canMoveNodeToIndex(parent, index, activePMNode);
53
143
 
54
144
  //NOTE: This will block drop targets showing for nodes that are valid after transformation (i.e. expand -> nestedExpand)
55
145
  if (!canDrop && !isBlocksDragTargetDebug()) {
56
- prevNode = node;
146
+ if (fg('platform_editor_drag_and_drop_target_v2')) {
147
+ pushNodeStack(node, depth);
148
+ } else {
149
+ prevNode = node;
150
+ }
57
151
  return false; //not valid pos, so nested not valid either
58
152
  }
59
153
  if (parent.lastChild === node && !isEmptyParagraph(node) && PARENT_WITH_END_DROP_TARGET.includes(parent.type.name)) {
60
154
  endPos = pos + node.nodeSize;
61
155
  }
62
156
  }
63
- const previousNode = prevNode; // created scoped variable
64
- decs.push(createDropTargetDecoration(pos, getPos => /*#__PURE__*/createElement(DropTarget, {
157
+ const previousNode = fg('platform_editor_drag_and_drop_target_v2') ? popNodeStack(depth) : prevNode; // created scoped variable
158
+ decs.push(createDropTargetDecoration(pos, {
65
159
  api,
66
- getPos,
67
160
  prevNode: previousNode,
68
161
  nextNode: node,
69
- parentNode: parent,
162
+ parentNode: parent || undefined,
70
163
  formatMessage
71
- })));
164
+ }, -1, anchorHeightsCache));
72
165
  if (endPos !== undefined) {
73
- decs.push(createDropTargetDecoration(endPos, getPos => /*#__PURE__*/createElement(DropTarget, {
166
+ decs.push(createDropTargetDecoration(endPos, {
74
167
  api,
75
- getPos,
76
- parentNode: parent,
168
+ prevNode: fg('platform_editor_drag_and_drop_target_v2') ? node : undefined,
169
+ parentNode: parent || undefined,
77
170
  formatMessage
78
- })));
171
+ }, -1, anchorHeightsCache));
79
172
  }
80
- prevNode = node;
81
- return depth < getNestedDepth();
173
+ if (fg('platform_editor_drag_and_drop_target_v2')) {
174
+ pushNodeStack(node, depth);
175
+ } else {
176
+ prevNode = node;
177
+ }
178
+ return depth < getNestedDepth() && shouldDescend(node);
82
179
  });
83
-
84
- //TODO: Should this use createDropTargetDecoration?
85
- decs.push(Decoration.widget(newState.doc.nodeSize - 2, (_, getPos) => {
86
- const element = document.createElement('div');
87
- element.setAttribute('data-blocks-drop-target-container', 'true');
88
- ReactDOM.render( /*#__PURE__*/createElement(DropTarget, {
89
- api,
90
- getPos,
91
- formatMessage
92
- }), element);
93
- return element;
94
- }, {
95
- type: 'drop-target-decoration'
96
- }));
180
+ decs.push(createDropTargetDecoration(newState.doc.nodeSize - 2, {
181
+ api,
182
+ formatMessage,
183
+ prevNode: newState.doc.lastChild || undefined,
184
+ parentNode: newState.doc
185
+ }, undefined, anchorHeightsCache));
97
186
  return decs;
98
187
  };
99
188
  export const emptyParagraphNodeDecorations = () => {
@@ -11,6 +11,7 @@ import { autoScrollForElements } from '@atlaskit/pragmatic-drag-and-drop-auto-sc
11
11
  import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
12
12
  import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
13
13
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
14
+ import { AnchorHeightsCache, isAnchorSupported } from '../utils/anchor-utils';
14
15
  import { isBlocksDragTargetDebug } from '../utils/drag-target-debug';
15
16
  import { dragHandleDecoration, dropTargetDecorations, emptyParagraphNodeDecorations, nodeDecorations } from './decorations';
16
17
  import { handleMouseOver } from './handle-mouse-over';
@@ -97,6 +98,10 @@ export const createPlugin = (api, getIntl) => {
97
98
  if (fg('platform_editor_element_dnd_nested_fix_patch_2')) {
98
99
  // TODO: Remove this once FG is used in code
99
100
  }
101
+ let anchorHeightsCache;
102
+ if (!isAnchorSupported() && fg('platform_editor_drag_and_drop_target_v2')) {
103
+ anchorHeightsCache = new AnchorHeightsCache();
104
+ }
100
105
  return new SafePlugin({
101
106
  key,
102
107
  state: {
@@ -244,7 +249,7 @@ export const createPlugin = (api, getIntl) => {
244
249
  // if the transaction is only for analytics and user is dragging, continue to draw drop targets
245
250
  if (shouldCreateDropTargets || isBlocksDragTargetDebug()) {
246
251
  var _meta$activeNode6;
247
- const decs = dropTargetDecorations(newState, api, formatMessage, isNestedEnabled ? (_meta$activeNode6 = meta === null || meta === void 0 ? void 0 : meta.activeNode) !== null && _meta$activeNode6 !== void 0 ? _meta$activeNode6 : mappedActiveNodePos : meta === null || meta === void 0 ? void 0 : meta.activeNode);
252
+ const decs = dropTargetDecorations(newState, api, formatMessage, isNestedEnabled ? (_meta$activeNode6 = meta === null || meta === void 0 ? void 0 : meta.activeNode) !== null && _meta$activeNode6 !== void 0 ? _meta$activeNode6 : mappedActiveNodePos : meta === null || meta === void 0 ? void 0 : meta.activeNode, anchorHeightsCache);
248
253
  decorations = decorations.add(newState.doc, decs);
249
254
  }
250
255
  }
@@ -319,6 +324,8 @@ export const createPlugin = (api, getIntl) => {
319
324
  return false;
320
325
  },
321
326
  dragstart(view) {
327
+ var _anchorHeightsCache;
328
+ (_anchorHeightsCache = anchorHeightsCache) === null || _anchorHeightsCache === void 0 ? void 0 : _anchorHeightsCache.setEditorView(view);
322
329
  view.dispatch(view.state.tr.setMeta(key, {
323
330
  isPMDragging: true
324
331
  }));
@@ -116,7 +116,7 @@ export const nodeMargins = {
116
116
  bottom: 0
117
117
  },
118
118
  codeBlock: {
119
- top: 0,
119
+ top: 12,
120
120
  bottom: 0
121
121
  },
122
122
  panel: {
@@ -124,8 +124,8 @@ export const nodeMargins = {
124
124
  bottom: 0
125
125
  },
126
126
  rule: {
127
- top: 20,
128
- bottom: 20
127
+ top: 24,
128
+ bottom: 24
129
129
  },
130
130
  mediaSingle: {
131
131
  top: 24,
@@ -0,0 +1,171 @@
1
+ /**
2
+ * @jsxRuntime classic
3
+ * @jsx jsx
4
+ */
5
+ import { Fragment, useEffect, useMemo, useRef, useState } from 'react';
6
+
7
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
8
+ import { css, jsx } from '@emotion/react';
9
+ import { useSharedPluginState } from '@atlaskit/editor-common/hooks';
10
+ import { DropIndicator } from '@atlaskit/pragmatic-drag-and-drop-react-drop-indicator/box';
11
+ import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
12
+ import { layers } from '@atlaskit/theme/constants';
13
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
14
+ import { getNodeAnchor } from '../pm-plugins/decorations';
15
+ import { isAnchorSupported } from '../utils/anchor-utils';
16
+ import { isBlocksDragTargetDebug } from '../utils/drag-target-debug';
17
+ import { getNestedNodeLeftPaddingMargin } from './consts';
18
+ const DEFAULT_DROP_INDICATOR_WIDTH = 760;
19
+ const EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_WIDTH = '--editor-block-controls-drop-indicator-width';
20
+ const EDITOR_BLOCK_CONTROLS_DROP_TARGET_LEFT_MARGIN = '--editor-block-controls-drop-target-leftMargin';
21
+ const EDITOR_BLOCK_CONTROLS_DROP_TARGET_ZINDEX = '--editor-block-controls-drop-target-zindex';
22
+ export const EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_OFFSET = '--editor-block-controls-drop-indicator-offset';
23
+ export const EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_GAP = '--editor-block-controls-drop-indicator-gap';
24
+ const styleDropTarget = css({
25
+ marginLeft: `calc(-1 * var(${EDITOR_BLOCK_CONTROLS_DROP_TARGET_LEFT_MARGIN}, 0))`,
26
+ paddingLeft: `var(${EDITOR_BLOCK_CONTROLS_DROP_TARGET_LEFT_MARGIN}, 0)`,
27
+ position: 'absolute',
28
+ left: '0',
29
+ display: 'block',
30
+ zIndex: `var(${EDITOR_BLOCK_CONTROLS_DROP_TARGET_ZINDEX}, 110)`,
31
+ transform: `translateY(var(${EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_OFFSET}, 0))`
32
+ });
33
+ const styleDropIndicator = css({
34
+ height: '100%',
35
+ margin: '0 auto',
36
+ position: 'relative',
37
+ width: `var(${EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_WIDTH}, 100%)`
38
+ });
39
+ const nestedDropIndicatorStyle = css({
40
+ position: 'relative'
41
+ });
42
+ const dropZoneStyles = css({
43
+ margin: 0,
44
+ position: 'absolute',
45
+ width: '100%',
46
+ zIndex: 110,
47
+ minHeight: '4px'
48
+ });
49
+ const nestedDropZoneStyle = css({
50
+ left: '4px',
51
+ right: '4px',
52
+ width: 'unset'
53
+ });
54
+ const enableDropZone = ['paragraph', 'mediaSingle', 'heading', 'codeBlock', 'decisionList', 'bulletList', 'orderedList', 'taskList', 'extension', 'blockCard'];
55
+ const HoverZone = ({
56
+ onDragEnter,
57
+ onDragLeave,
58
+ onDrop,
59
+ node,
60
+ editorWidth,
61
+ anchorHeightsCache,
62
+ position,
63
+ isNestedDropTarget
64
+ }) => {
65
+ const ref = useRef(null);
66
+ useEffect(() => {
67
+ if (ref.current) {
68
+ return dropTargetForElements({
69
+ element: ref.current,
70
+ onDragEnter,
71
+ onDragLeave,
72
+ onDrop
73
+ });
74
+ }
75
+ }, [onDragEnter, onDragLeave, onDrop]);
76
+ const hoverZoneUpperStyle = useMemo(() => {
77
+ const anchorName = node ? getNodeAnchor(node) : '';
78
+ const heightStyleOffset = `var(--editor-block-controls-drop-indicator-gap, 0)/2`;
79
+ const transformOffset = `var(${EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_OFFSET}, 0)`;
80
+ const heightStyle = anchorName && enableDropZone.includes((node === null || node === void 0 ? void 0 : node.type.name) || '') ? isAnchorSupported() ? `calc(anchor-size(${anchorName} height)/2 + ${heightStyleOffset})` : `calc(${((anchorHeightsCache === null || anchorHeightsCache === void 0 ? void 0 : anchorHeightsCache.getHeight(anchorName)) || 0) / 2}px + ${heightStyleOffset})` : '4px';
81
+ const transform = position === 'upper' ? `translateY(calc(-100% + ${transformOffset}))` : `translateY(${transformOffset})`;
82
+ return css({
83
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values
84
+ height: heightStyle,
85
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values
86
+ transform: transform,
87
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values
88
+ maxWidth: `${editorWidth || 0}px`
89
+ });
90
+ }, [anchorHeightsCache, editorWidth, node, position]);
91
+ return jsx("div", {
92
+ ref: ref
93
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
94
+ ,
95
+ className: `drop-target-hover-zone-${position}`
96
+ // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
97
+ ,
98
+ css: [dropZoneStyles, isNestedDropTarget && nestedDropZoneStyle, hoverZoneUpperStyle]
99
+ });
100
+ };
101
+ export const DropTargetV2 = ({
102
+ api,
103
+ getPos,
104
+ prevNode,
105
+ nextNode,
106
+ parentNode,
107
+ formatMessage,
108
+ anchorHeightsCache
109
+ }) => {
110
+ const [isDraggedOver, setIsDraggedOver] = useState(false);
111
+ const {
112
+ widthState
113
+ } = useSharedPluginState(api, ['width']);
114
+ const isNestedDropTarget = (parentNode === null || parentNode === void 0 ? void 0 : parentNode.type.name) !== 'doc';
115
+ const onDrop = () => {
116
+ var _api$blockControls;
117
+ const {
118
+ activeNode
119
+ } = (api === null || api === void 0 ? void 0 : (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.sharedState.currentState()) || {};
120
+ if (!activeNode) {
121
+ return;
122
+ }
123
+ const pos = getPos();
124
+ if (activeNode && pos !== undefined) {
125
+ var _api$core, _api$blockControls2, _api$blockControls2$c;
126
+ const {
127
+ pos: start
128
+ } = activeNode;
129
+ 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));
130
+ }
131
+ };
132
+ const dynamicStyle = {
133
+ width: isNestedDropTarget ? 'unset' : '100%',
134
+ [EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_WIDTH]: isNestedDropTarget ? '100%' : `${(widthState === null || widthState === void 0 ? void 0 : widthState.lineLength) || DEFAULT_DROP_INDICATOR_WIDTH}px`,
135
+ [EDITOR_BLOCK_CONTROLS_DROP_TARGET_LEFT_MARGIN]: isNestedDropTarget ? getNestedNodeLeftPaddingMargin(parentNode === null || parentNode === void 0 ? void 0 : parentNode.type.name) : '0',
136
+ [EDITOR_BLOCK_CONTROLS_DROP_TARGET_ZINDEX]: editorExperiment('nested-dnd', true) ? layers.navigation() : layers.card()
137
+ };
138
+ return jsx(Fragment, null, jsx(HoverZone, {
139
+ onDragEnter: () => setIsDraggedOver(true),
140
+ onDragLeave: () => setIsDraggedOver(false),
141
+ onDrop: onDrop,
142
+ node: prevNode,
143
+ editorWidth: widthState === null || widthState === void 0 ? void 0 : widthState.lineLength,
144
+ anchorHeightsCache: anchorHeightsCache,
145
+ position: "upper",
146
+ isNestedDropTarget: isNestedDropTarget
147
+ }), jsx("div", {
148
+ // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
149
+ css: [styleDropTarget, isNestedDropTarget && nestedDropIndicatorStyle]
150
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop
151
+ ,
152
+ style: dynamicStyle,
153
+ "data-testid": "block-ctrl-drop-target"
154
+ },
155
+ // 4px gap to clear expand node border
156
+ (isDraggedOver || isBlocksDragTargetDebug()) && jsx("div", {
157
+ css: styleDropIndicator,
158
+ "data-testid": "block-ctrl-drop-indicator"
159
+ }, jsx(DropIndicator, {
160
+ edge: "bottom"
161
+ }))), jsx(HoverZone, {
162
+ onDragEnter: () => setIsDraggedOver(true),
163
+ onDragLeave: () => setIsDraggedOver(false),
164
+ onDrop: onDrop,
165
+ node: nextNode,
166
+ editorWidth: widthState === null || widthState === void 0 ? void 0 : widthState.lineLength,
167
+ anchorHeightsCache: anchorHeightsCache,
168
+ position: "lower",
169
+ isNestedDropTarget: isNestedDropTarget
170
+ }));
171
+ };
@@ -0,0 +1,51 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import memoizeOne from 'memoize-one';
3
+ export const isAnchorSupported = memoizeOne(() => {
4
+ // directly use CSS would cause failed SSR tests.
5
+ if (window.CSS && window.CSS.supports) {
6
+ return window.CSS.supports('anchor-name: --a');
7
+ }
8
+ return false;
9
+ });
10
+ export class AnchorHeightsCache {
11
+ constructor() {
12
+ _defineProperty(this, "anchorHeightsMap", {});
13
+ _defineProperty(this, "isAnchorSupported", isAnchorSupported());
14
+ _defineProperty(this, "isDirty", true);
15
+ _defineProperty(this, "view", null);
16
+ }
17
+ clear() {
18
+ this.isDirty = true;
19
+ this.anchorHeightsMap = {};
20
+ }
21
+ getHeights() {
22
+ if (this.isDirty) {
23
+ var _this$view;
24
+ const anchorElements = ((_this$view = this.view) === null || _this$view === void 0 ? void 0 : _this$view.dom.querySelectorAll('[data-drag-handler-anchor-name]')) || [];
25
+ this.anchorHeightsMap = Array.from(anchorElements).reduce((prev, curr) => {
26
+ const anchorName = curr.getAttribute('data-drag-handler-anchor-name');
27
+ if (anchorName) {
28
+ return {
29
+ ...prev,
30
+ [anchorName]: curr.clientHeight
31
+ };
32
+ }
33
+ return prev;
34
+ }, {});
35
+ this.isDirty = false;
36
+ }
37
+ return this.anchorHeightsMap;
38
+ }
39
+ setEditorView(view) {
40
+ if (this.view !== view) {
41
+ this.view = view;
42
+ }
43
+ }
44
+ getHeight(anchorName) {
45
+ if (this.isAnchorSupported) {
46
+ return null;
47
+ }
48
+ const heights = this.getHeights();
49
+ return heights[anchorName];
50
+ }
51
+ }