@atlaskit/editor-plugin-block-controls 2.9.0 → 2.10.1
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 +19 -0
- package/dist/cjs/commands/move-to-layout.js +16 -1
- package/dist/cjs/pm-plugins/decorations-anchor.js +99 -0
- package/dist/cjs/pm-plugins/decorations-common.js +49 -0
- package/dist/cjs/pm-plugins/decorations-drag-handle.js +88 -0
- package/dist/cjs/pm-plugins/{decorations.js → decorations-drop-target.js} +11 -208
- package/dist/cjs/pm-plugins/main.js +21 -19
- package/dist/cjs/ui/drop-target-v2.js +3 -3
- package/dist/cjs/ui/inline-drop-target.js +79 -20
- package/dist/cjs/utils/check-media-layout.js +14 -0
- package/dist/cjs/utils/inline-drop-target.js +4 -0
- package/dist/es2019/commands/move-to-layout.js +16 -1
- package/dist/es2019/pm-plugins/decorations-anchor.js +91 -0
- package/dist/es2019/pm-plugins/decorations-common.js +31 -0
- package/dist/es2019/pm-plugins/decorations-drag-handle.js +79 -0
- package/dist/es2019/pm-plugins/{decorations.js → decorations-drop-target.js} +6 -192
- package/dist/es2019/pm-plugins/main.js +3 -1
- package/dist/es2019/ui/drop-target-v2.js +1 -1
- package/dist/es2019/ui/inline-drop-target.js +77 -18
- package/dist/es2019/utils/check-media-layout.js +8 -0
- package/dist/es2019/utils/inline-drop-target.js +4 -0
- package/dist/esm/commands/move-to-layout.js +16 -1
- package/dist/esm/pm-plugins/decorations-anchor.js +92 -0
- package/dist/esm/pm-plugins/decorations-common.js +42 -0
- package/dist/esm/pm-plugins/decorations-drag-handle.js +81 -0
- package/dist/esm/pm-plugins/{decorations.js → decorations-drop-target.js} +6 -203
- package/dist/esm/pm-plugins/main.js +3 -1
- package/dist/esm/ui/drop-target-v2.js +1 -1
- package/dist/esm/ui/inline-drop-target.js +77 -18
- package/dist/esm/utils/check-media-layout.js +8 -0
- package/dist/esm/utils/inline-drop-target.js +4 -0
- package/dist/types/pm-plugins/decorations-anchor.d.ts +13 -0
- package/dist/types/pm-plugins/decorations-common.d.ts +7 -0
- package/dist/types/pm-plugins/decorations-drag-handle.d.ts +7 -0
- package/dist/types/pm-plugins/decorations-drop-target.d.ts +17 -0
- package/dist/types/ui/inline-drop-target.d.ts +6 -1
- package/dist/types/utils/check-media-layout.d.ts +2 -0
- package/dist/types-ts4.5/pm-plugins/decorations-anchor.d.ts +13 -0
- package/dist/types-ts4.5/pm-plugins/decorations-common.d.ts +7 -0
- package/dist/types-ts4.5/pm-plugins/decorations-drag-handle.d.ts +7 -0
- package/dist/types-ts4.5/pm-plugins/decorations-drop-target.d.ts +17 -0
- package/dist/types-ts4.5/ui/inline-drop-target.d.ts +6 -1
- package/dist/types-ts4.5/utils/check-media-layout.d.ts +2 -0
- package/package.json +6 -6
- package/dist/types/pm-plugins/decorations.d.ts +0 -35
- package/dist/types-ts4.5/pm-plugins/decorations.d.ts +0 -35
|
@@ -1,29 +1,23 @@
|
|
|
1
|
-
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
1
|
import { createElement } from 'react';
|
|
3
|
-
import { bind } from 'bind-event-listener';
|
|
4
2
|
import ReactDOM from 'react-dom';
|
|
5
|
-
import uuid from 'uuid';
|
|
6
3
|
import { isEmptyParagraph } from '@atlaskit/editor-common/utils';
|
|
7
4
|
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
8
5
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
9
6
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
10
7
|
import { nodeMargins } from '../ui/consts';
|
|
11
|
-
import { DragHandle } from '../ui/drag-handle';
|
|
12
8
|
import { DropTarget } from '../ui/drop-target';
|
|
13
9
|
import { DropTargetV2, EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_GAP, EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_OFFSET } from '../ui/drop-target-v2';
|
|
14
10
|
import { isBlocksDragTargetDebug } from '../utils/drag-target-debug';
|
|
15
11
|
import { canMoveNodeToIndex } from '../utils/validation';
|
|
12
|
+
import { getNestedDepth, TYPE_DROP_TARGET_DEC, unmountDecorations } from './decorations-common';
|
|
16
13
|
const IGNORE_NODES = ['tableCell', 'tableHeader', 'tableRow', 'layoutColumn', 'listItem', 'caption'];
|
|
17
|
-
const IGNORE_NODE_DESCENDANTS = ['listItem', 'taskList', 'decisionList', 'mediaSingle'];
|
|
18
14
|
const PARENT_WITH_END_DROP_TARGET = ['tableCell', 'tableHeader', 'panel', 'layoutColumn', 'expand', 'nestedExpand', 'bodiedExtension'];
|
|
19
15
|
const DISABLE_CHILD_DROP_TARGET = ['orderedList', 'bulletList'];
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const handleId = ObjHash.getForNode(node);
|
|
26
|
-
return `--node-anchor-${node.type.name}-${handleId}`;
|
|
16
|
+
const shouldDescend = node => {
|
|
17
|
+
if (fg('platform_editor_drag_and_drop_target_v2')) {
|
|
18
|
+
return !['mediaSingle', 'paragraph', 'heading'].includes(node.type.name);
|
|
19
|
+
}
|
|
20
|
+
return true;
|
|
27
21
|
};
|
|
28
22
|
const getNodeMargins = node => {
|
|
29
23
|
if (!node) {
|
|
@@ -68,12 +62,6 @@ const getGapAndOffset = (prevNode, nextNode, parentNode) => {
|
|
|
68
62
|
offset
|
|
69
63
|
};
|
|
70
64
|
};
|
|
71
|
-
const shouldDescend = node => {
|
|
72
|
-
if (fg('platform_editor_drag_and_drop_target_v2')) {
|
|
73
|
-
return !['mediaSingle', 'paragraph', 'heading'].includes(node.type.name);
|
|
74
|
-
}
|
|
75
|
-
return true;
|
|
76
|
-
};
|
|
77
65
|
|
|
78
66
|
/**
|
|
79
67
|
* Find drop target decorations in the pos range between from and to
|
|
@@ -85,37 +73,6 @@ const shouldDescend = node => {
|
|
|
85
73
|
export const findDropTargetDecs = (decorations, from, to) => {
|
|
86
74
|
return decorations.find(from, to, spec => spec.type === TYPE_DROP_TARGET_DEC);
|
|
87
75
|
};
|
|
88
|
-
export const findHandleDec = (decorations, from, to) => {
|
|
89
|
-
return decorations.find(from, to, spec => spec.type === TYPE_HANDLE_DEC);
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Find node decorations in the pos range between from and to (non-inclusive)
|
|
94
|
-
* @param decorations
|
|
95
|
-
* @param from
|
|
96
|
-
* @param to
|
|
97
|
-
* @returns
|
|
98
|
-
*/
|
|
99
|
-
export const findNodeDecs = (decorations, from, to) => {
|
|
100
|
-
let newfrom = from;
|
|
101
|
-
let newTo = to;
|
|
102
|
-
|
|
103
|
-
// make it non-inclusive
|
|
104
|
-
if (newfrom !== undefined) {
|
|
105
|
-
newfrom++;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// make it non-inclusive
|
|
109
|
-
if (newTo !== undefined) {
|
|
110
|
-
newTo--;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// return empty array if range reversed
|
|
114
|
-
if (newfrom !== undefined && newTo !== undefined && newfrom > newTo) {
|
|
115
|
-
return new Array();
|
|
116
|
-
}
|
|
117
|
-
return decorations.find(newfrom, newTo, spec => spec.type === TYPE_NODE_DEC);
|
|
118
|
-
};
|
|
119
76
|
export const createDropTargetDecoration = (pos, props, side, anchorRectCache) => {
|
|
120
77
|
return Decoration.widget(pos, (_, getPos) => {
|
|
121
78
|
const element = document.createElement('div');
|
|
@@ -244,147 +201,4 @@ export const dropTargetDecorations = (newState, api, formatMessage, activeNode,
|
|
|
244
201
|
}, undefined, anchorRectCache));
|
|
245
202
|
}
|
|
246
203
|
return decs;
|
|
247
|
-
};
|
|
248
|
-
export const emptyParagraphNodeDecorations = () => {
|
|
249
|
-
const anchorName = `--node-anchor-paragraph-0`;
|
|
250
|
-
const style = `anchor-name: ${anchorName}; margin-top: 0px;`;
|
|
251
|
-
return Decoration.node(0, 2, {
|
|
252
|
-
style,
|
|
253
|
-
['data-drag-handler-anchor-name']: anchorName
|
|
254
|
-
}, {
|
|
255
|
-
type: TYPE_NODE_DEC
|
|
256
|
-
});
|
|
257
|
-
};
|
|
258
|
-
class ObjHash {
|
|
259
|
-
static getForNode(node) {
|
|
260
|
-
if (this.caching.has(node)) {
|
|
261
|
-
return this.caching.get(node);
|
|
262
|
-
}
|
|
263
|
-
const uniqueId = uuid();
|
|
264
|
-
this.caching.set(node, uniqueId);
|
|
265
|
-
return uniqueId;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
_defineProperty(ObjHash, "caching", new WeakMap());
|
|
269
|
-
const shouldIgnoreNode = node => {
|
|
270
|
-
if ('mediaSingle' === node.type.name && fg('platform_editor_element_dnd_nested_fix_patch_1')) {
|
|
271
|
-
if (['wrap-right', 'wrap-left'].includes(node.attrs.layout)) {
|
|
272
|
-
return true;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
return IGNORE_NODES.includes(node.type.name);
|
|
276
|
-
};
|
|
277
|
-
export const shouldDescendIntoNode = node => {
|
|
278
|
-
// Optimisation to avoid drawing node decorations for empty table cells
|
|
279
|
-
if (['tableCell', 'tableHeader'].includes(node.type.name) && !editorExperiment('table-nested-dnd', true) && fg('platform_editor_element_dnd_nested_fix_patch_3')) {
|
|
280
|
-
var _node$firstChild;
|
|
281
|
-
if (node.childCount === 1 && ((_node$firstChild = node.firstChild) === null || _node$firstChild === void 0 ? void 0 : _node$firstChild.type.name) === 'paragraph') {
|
|
282
|
-
return false;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
return !IGNORE_NODE_DESCENDANTS.includes(node.type.name);
|
|
286
|
-
};
|
|
287
|
-
export const nodeDecorations = (newState, from, to) => {
|
|
288
|
-
const decs = [];
|
|
289
|
-
const docFrom = from === undefined || from < 0 ? 0 : from;
|
|
290
|
-
const docTo = to === undefined || to > newState.doc.nodeSize - 2 ? newState.doc.nodeSize - 2 : to;
|
|
291
|
-
newState.doc.nodesBetween(docFrom, docTo, (node, pos, _parent, index) => {
|
|
292
|
-
let depth = 0;
|
|
293
|
-
let anchorName;
|
|
294
|
-
const shouldDescend = shouldDescendIntoNode(node);
|
|
295
|
-
const handleId = ObjHash.getForNode(node);
|
|
296
|
-
anchorName = `--node-anchor-${node.type.name}-${handleId}`;
|
|
297
|
-
if (editorExperiment('nested-dnd', true)) {
|
|
298
|
-
var _anchorName;
|
|
299
|
-
// Doesn't descend into a node
|
|
300
|
-
if (node.isInline) {
|
|
301
|
-
return false;
|
|
302
|
-
}
|
|
303
|
-
if (shouldIgnoreNode(node)) {
|
|
304
|
-
return shouldDescend; //skip over, don't consider it a valid depth
|
|
305
|
-
}
|
|
306
|
-
depth = newState.doc.resolve(pos).depth;
|
|
307
|
-
anchorName = (_anchorName = anchorName) !== null && _anchorName !== void 0 ? _anchorName : `--node-anchor-${node.type.name}-${pos}`;
|
|
308
|
-
} else {
|
|
309
|
-
var _anchorName2;
|
|
310
|
-
anchorName = (_anchorName2 = anchorName) !== null && _anchorName2 !== void 0 ? _anchorName2 : `--node-anchor-${node.type.name}-${index}`;
|
|
311
|
-
}
|
|
312
|
-
decs.push(Decoration.node(pos, pos + node.nodeSize, {
|
|
313
|
-
style: `anchor-name: ${anchorName}; ${pos === 0 ? 'margin-top: 0px;' : ''}; position: relative; z-index: 1;`,
|
|
314
|
-
['data-drag-handler-anchor-name']: anchorName,
|
|
315
|
-
['data-drag-handler-node-type']: node.type.name,
|
|
316
|
-
['data-drag-handler-anchor-depth']: `${depth}`
|
|
317
|
-
}, {
|
|
318
|
-
type: TYPE_NODE_DEC,
|
|
319
|
-
anchorName,
|
|
320
|
-
nodeType: node.type.name
|
|
321
|
-
}));
|
|
322
|
-
return shouldDescend && depth < getNestedDepth();
|
|
323
|
-
});
|
|
324
|
-
return decs;
|
|
325
|
-
};
|
|
326
|
-
export const dragHandleDecoration = (api, formatMessage, pos, anchorName, nodeType, handleOptions) => {
|
|
327
|
-
unmountDecorations('data-blocks-drag-handle-container');
|
|
328
|
-
let unbind;
|
|
329
|
-
return Decoration.widget(pos, (view, getPos) => {
|
|
330
|
-
const element = document.createElement('span');
|
|
331
|
-
// Need to set it to inline to avoid text being split when merging two paragraphs
|
|
332
|
-
// platform_editor_element_dnd_nested_fix_patch_2 -> inline decoration causes focus issues when refocusing Editor into first line
|
|
333
|
-
element.style.display = fg('platform_editor_element_dnd_nested_fix_patch_2') ? 'block' : 'inline';
|
|
334
|
-
element.setAttribute('data-testid', 'block-ctrl-decorator-widget');
|
|
335
|
-
element.setAttribute('data-blocks-drag-handle-container', 'true');
|
|
336
|
-
let isTopLevelNode = true;
|
|
337
|
-
if (editorExperiment('nested-dnd', true)) {
|
|
338
|
-
const newPos = fg('platform_editor_element_dnd_nested_fix_patch_3') ? getPos() : pos;
|
|
339
|
-
if (typeof newPos === 'number') {
|
|
340
|
-
const $pos = view.state.doc.resolve(newPos);
|
|
341
|
-
isTopLevelNode = ($pos === null || $pos === void 0 ? void 0 : $pos.parent.type.name) === 'doc';
|
|
342
|
-
}
|
|
343
|
-
/*
|
|
344
|
-
* We disable mouseover event to fix flickering issue on hover
|
|
345
|
-
* However, the tooltip for nested drag handle is no long working.
|
|
346
|
-
*/
|
|
347
|
-
if (newPos === undefined || !isTopLevelNode) {
|
|
348
|
-
// This will also hide the tooltip.
|
|
349
|
-
unbind = bind(element, {
|
|
350
|
-
type: 'mouseover',
|
|
351
|
-
listener: e => {
|
|
352
|
-
e.stopPropagation();
|
|
353
|
-
}
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// There are times when global clear: "both" styles are applied to this decoration causing jumpiness
|
|
359
|
-
// due to margins applied to other nodes eg. Headings
|
|
360
|
-
element.style.clear = 'unset';
|
|
361
|
-
ReactDOM.render( /*#__PURE__*/createElement(DragHandle, {
|
|
362
|
-
view,
|
|
363
|
-
api,
|
|
364
|
-
formatMessage,
|
|
365
|
-
getPos,
|
|
366
|
-
anchorName,
|
|
367
|
-
nodeType,
|
|
368
|
-
handleOptions,
|
|
369
|
-
isTopLevelNode
|
|
370
|
-
}), element);
|
|
371
|
-
return element;
|
|
372
|
-
}, {
|
|
373
|
-
side: -1,
|
|
374
|
-
type: TYPE_HANDLE_DEC,
|
|
375
|
-
testid: `${TYPE_HANDLE_DEC}-${uuid()}`,
|
|
376
|
-
destroy: () => {
|
|
377
|
-
if (editorExperiment('nested-dnd', true)) {
|
|
378
|
-
unbind && unbind();
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
});
|
|
382
|
-
};
|
|
383
|
-
const unmountDecorations = selector => {
|
|
384
|
-
// Removing decorations manually instead of using native destroy function in prosemirror API
|
|
385
|
-
// as it was more responsive and causes less re-rendering
|
|
386
|
-
const decorationsToRemove = document.querySelectorAll(`[${selector}="true"]`);
|
|
387
|
-
decorationsToRemove.forEach(el => {
|
|
388
|
-
ReactDOM.unmountComponentAtNode(el);
|
|
389
|
-
});
|
|
390
204
|
};
|
|
@@ -14,7 +14,9 @@ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
|
14
14
|
import { AnchorRectCache, isAnchorSupported } from '../utils/anchor-utils';
|
|
15
15
|
import { isBlocksDragTargetDebug } from '../utils/drag-target-debug';
|
|
16
16
|
import { getTrMetadata } from '../utils/transactions';
|
|
17
|
-
import {
|
|
17
|
+
import { findNodeDecs, nodeDecorations } from './decorations-anchor';
|
|
18
|
+
import { dragHandleDecoration, emptyParagraphNodeDecorations, findHandleDec } from './decorations-drag-handle';
|
|
19
|
+
import { dropTargetDecorations, findDropTargetDecs } from './decorations-drop-target';
|
|
18
20
|
import { handleMouseOver } from './handle-mouse-over';
|
|
19
21
|
import { boundKeydownHandler } from './keymap';
|
|
20
22
|
export const key = new PluginKey('blockControls');
|
|
@@ -12,7 +12,7 @@ import { DropIndicator } from '@atlaskit/pragmatic-drag-and-drop-react-drop-indi
|
|
|
12
12
|
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
13
13
|
import { layers } from '@atlaskit/theme/constants';
|
|
14
14
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
15
|
-
import { getNodeAnchor } from '../pm-plugins/decorations';
|
|
15
|
+
import { getNodeAnchor } from '../pm-plugins/decorations-common';
|
|
16
16
|
import { isAnchorSupported } from '../utils/anchor-utils';
|
|
17
17
|
import { isBlocksDragTargetDebug } from '../utils/drag-target-debug';
|
|
18
18
|
import { shouldAllowInlineDropTarget } from '../utils/inline-drop-target';
|
|
@@ -11,19 +11,17 @@ import { css, jsx } from '@emotion/react';
|
|
|
11
11
|
import { useSharedPluginState } from '@atlaskit/editor-common/hooks';
|
|
12
12
|
import { DropIndicator } from '@atlaskit/pragmatic-drag-and-drop-react-drop-indicator/box';
|
|
13
13
|
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
14
|
-
import { getNodeAnchor } from '../pm-plugins/decorations';
|
|
14
|
+
import { getNodeAnchor } from '../pm-plugins/decorations-common';
|
|
15
15
|
import { isAnchorSupported } from '../utils/anchor-utils';
|
|
16
|
+
import { isBlocksDragTargetDebug } from '../utils/drag-target-debug';
|
|
16
17
|
const dropTargetCommonStyle = css({
|
|
17
18
|
position: 'absolute',
|
|
18
19
|
display: 'block'
|
|
19
20
|
});
|
|
20
|
-
const hideDropTargetStyle = css({
|
|
21
|
-
display: 'none'
|
|
22
|
-
});
|
|
23
21
|
const hoverZoneCommonStyle = css({
|
|
24
22
|
position: 'absolute',
|
|
25
|
-
//
|
|
26
|
-
zIndex:
|
|
23
|
+
// above the top and bottom drop zone as block hover zone
|
|
24
|
+
zIndex: 120
|
|
27
25
|
});
|
|
28
26
|
|
|
29
27
|
// gap between node boundary and drop indicator/drop zone
|
|
@@ -31,6 +29,60 @@ const GAP = 4;
|
|
|
31
29
|
const HOVER_ZONE_WIDTH_OFFSET = 40;
|
|
32
30
|
const HOVER_ZONE_HEIGHT_OFFSET = 10;
|
|
33
31
|
const HOVER_ZONE_DEFAULT_WIDTH = 40;
|
|
32
|
+
const getDropTargetPositionOverride = (node, editorWidth) => {
|
|
33
|
+
if (!node || !editorWidth) {
|
|
34
|
+
return {
|
|
35
|
+
left: 0,
|
|
36
|
+
right: 0
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const getOffsets = nodeWidth => {
|
|
40
|
+
const offset = (editorWidth - nodeWidth) / 2;
|
|
41
|
+
return {
|
|
42
|
+
left: offset,
|
|
43
|
+
right: offset
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
if ((node === null || node === void 0 ? void 0 : node.type.name) === 'table' && node.attrs.width) {
|
|
47
|
+
return getOffsets(node.attrs.width);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// media single 🤦
|
|
51
|
+
if ((node === null || node === void 0 ? void 0 : node.type.name) === 'mediaSingle') {
|
|
52
|
+
let mediaNodeWidth = 0;
|
|
53
|
+
if (node.attrs.width) {
|
|
54
|
+
if (node.attrs.widthType === 'pixel') {
|
|
55
|
+
mediaNodeWidth = node.attrs.width;
|
|
56
|
+
} else if (editorWidth) {
|
|
57
|
+
mediaNodeWidth = node.attrs.width / 100 * editorWidth;
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
// use media width
|
|
61
|
+
const mediaNode = node.firstChild;
|
|
62
|
+
if (mediaNode && mediaNode.attrs.width) {
|
|
63
|
+
mediaNodeWidth = mediaNode.attrs.width;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (mediaNodeWidth) {
|
|
67
|
+
if (node.attrs.layout === 'align-start') {
|
|
68
|
+
return {
|
|
69
|
+
left: 0,
|
|
70
|
+
right: editorWidth - mediaNodeWidth
|
|
71
|
+
};
|
|
72
|
+
} else if (node.attrs.layout === 'align-end') {
|
|
73
|
+
return {
|
|
74
|
+
left: editorWidth - mediaNodeWidth,
|
|
75
|
+
right: 0
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return getOffsets(mediaNodeWidth);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
left: 0,
|
|
83
|
+
right: 0
|
|
84
|
+
};
|
|
85
|
+
};
|
|
34
86
|
export const InlineDropTarget = ({
|
|
35
87
|
api,
|
|
36
88
|
nextNode,
|
|
@@ -49,22 +101,25 @@ export const InlineDropTarget = ({
|
|
|
49
101
|
const handleDragLeave = useCallback(() => {
|
|
50
102
|
setIsDraggedOver(false);
|
|
51
103
|
}, []);
|
|
104
|
+
const offsets = useMemo(() => {
|
|
105
|
+
return getDropTargetPositionOverride(nextNode, widthState === null || widthState === void 0 ? void 0 : widthState.lineLength);
|
|
106
|
+
}, [nextNode, widthState]);
|
|
52
107
|
const dropTargetRectStyle = useMemo(() => {
|
|
53
108
|
if (isAnchorSupported()) {
|
|
54
109
|
return css({
|
|
55
110
|
height: `calc(anchor-size(${anchorName} height))`,
|
|
56
111
|
positionAnchor: anchorName,
|
|
57
|
-
left: position === 'left' ? `calc(anchor(left) - ${GAP}px)` : `calc(anchor(right) + ${GAP}px)`,
|
|
112
|
+
left: position === 'left' ? `calc(anchor(left) - ${GAP - offsets.left}px)` : `calc(anchor(right) + ${GAP - offsets.right}px)`,
|
|
58
113
|
top: `calc(anchor(top))`
|
|
59
114
|
});
|
|
60
115
|
}
|
|
61
116
|
const nodeRect = anchorRectCache === null || anchorRectCache === void 0 ? void 0 : anchorRectCache.getRect(anchorName);
|
|
62
117
|
return css({
|
|
63
118
|
height: `calc(${(nodeRect === null || nodeRect === void 0 ? void 0 : nodeRect.height) || 0}px)`,
|
|
64
|
-
left: position === 'left' ? `${((nodeRect === null || nodeRect === void 0 ? void 0 : nodeRect.left) || 0) - GAP}px` : `${((nodeRect === null || nodeRect === void 0 ? void 0 : nodeRect.right) || 0) + GAP}px`,
|
|
119
|
+
left: position === 'left' ? `${((nodeRect === null || nodeRect === void 0 ? void 0 : nodeRect.left) || 0) - GAP + offsets.left}px` : `${((nodeRect === null || nodeRect === void 0 ? void 0 : nodeRect.right) || 0) + GAP - offsets.right}px`,
|
|
65
120
|
top: `${(nodeRect === null || nodeRect === void 0 ? void 0 : nodeRect.top) || 0}px`
|
|
66
121
|
});
|
|
67
|
-
}, [anchorName, anchorRectCache, position]);
|
|
122
|
+
}, [anchorName, anchorRectCache, offsets.left, offsets.right, position]);
|
|
68
123
|
const onDrop = useCallback(() => {
|
|
69
124
|
var _api$blockControls;
|
|
70
125
|
const {
|
|
@@ -84,8 +139,8 @@ export const InlineDropTarget = ({
|
|
|
84
139
|
}, [api, getPos, position]);
|
|
85
140
|
return jsx(Fragment, null, jsx("div", {
|
|
86
141
|
"data-test-id": `block-ctrl-drop-target-${position}`,
|
|
87
|
-
css: [dropTargetCommonStyle, dropTargetRectStyle
|
|
88
|
-
}, jsx(DropIndicator, {
|
|
142
|
+
css: [dropTargetCommonStyle, dropTargetRectStyle]
|
|
143
|
+
}, (isDraggedOver || isBlocksDragTargetDebug()) && jsx(DropIndicator, {
|
|
89
144
|
edge: position
|
|
90
145
|
})), jsx(InlineHoverZone, {
|
|
91
146
|
position: position,
|
|
@@ -94,7 +149,8 @@ export const InlineDropTarget = ({
|
|
|
94
149
|
anchorRectCache: anchorRectCache,
|
|
95
150
|
onDragEnter: handleDragEnter,
|
|
96
151
|
onDragLeave: handleDragLeave,
|
|
97
|
-
onDrop: onDrop
|
|
152
|
+
onDrop: onDrop,
|
|
153
|
+
offsets: offsets
|
|
98
154
|
}));
|
|
99
155
|
};
|
|
100
156
|
export const InlineHoverZone = ({
|
|
@@ -102,6 +158,7 @@ export const InlineHoverZone = ({
|
|
|
102
158
|
editorWidthState,
|
|
103
159
|
anchorRectCache,
|
|
104
160
|
position,
|
|
161
|
+
offsets,
|
|
105
162
|
onDragEnter,
|
|
106
163
|
onDragLeave,
|
|
107
164
|
onDrop
|
|
@@ -122,27 +179,29 @@ export const InlineHoverZone = ({
|
|
|
122
179
|
}
|
|
123
180
|
}, [onDragEnter, onDragLeave, onDrop]);
|
|
124
181
|
const inlineHoverZoneRectStyle = useMemo(() => {
|
|
182
|
+
const offset = offsets[position];
|
|
125
183
|
if (isAnchorSupported()) {
|
|
126
184
|
return css({
|
|
127
185
|
positionAnchor: anchorName,
|
|
128
|
-
left: position === 'left' ? 'unset' : `calc(anchor(right) + ${GAP}px)`,
|
|
129
|
-
right: position === 'left' ? `calc(anchor(left) + ${GAP}px)` : 'unset',
|
|
186
|
+
left: position === 'left' ? 'unset' : `calc(anchor(right) + ${GAP - offset}px)`,
|
|
187
|
+
right: position === 'left' ? `calc(anchor(left) + ${GAP - offset}px)` : 'unset',
|
|
130
188
|
top: `calc(anchor(top))`,
|
|
131
|
-
width: editorWith ? `calc((${editorWith}px - anchor-size(${anchorName} width))/2 - ${HOVER_ZONE_WIDTH_OFFSET}px)` : `${HOVER_ZONE_DEFAULT_WIDTH}px`,
|
|
189
|
+
width: editorWith ? `calc((${editorWith}px - anchor-size(${anchorName} width))/2 - ${HOVER_ZONE_WIDTH_OFFSET}px + ${offset}px)` : `${HOVER_ZONE_DEFAULT_WIDTH}px`,
|
|
132
190
|
height: `calc(anchor-size(${anchorName} height))`
|
|
133
191
|
});
|
|
134
192
|
}
|
|
135
193
|
const nodeRect = anchorRectCache === null || anchorRectCache === void 0 ? void 0 : anchorRectCache.getRect(anchorName);
|
|
136
|
-
const width = editorWith ? (editorWith - ((nodeRect === null || nodeRect === void 0 ? void 0 : nodeRect.width) || 0)) / 2 - HOVER_ZONE_WIDTH_OFFSET : HOVER_ZONE_DEFAULT_WIDTH;
|
|
194
|
+
const width = editorWith ? (editorWith - ((nodeRect === null || nodeRect === void 0 ? void 0 : nodeRect.width) || 0)) / 2 - HOVER_ZONE_WIDTH_OFFSET + offset : HOVER_ZONE_DEFAULT_WIDTH;
|
|
137
195
|
return css({
|
|
138
|
-
left: position === 'left' ? `${((nodeRect === null || nodeRect === void 0 ? void 0 : nodeRect.left) || 0) - width - GAP}px` : `${((nodeRect === null || nodeRect === void 0 ? void 0 : nodeRect.right) || 0) + GAP}px`,
|
|
196
|
+
left: position === 'left' ? `${((nodeRect === null || nodeRect === void 0 ? void 0 : nodeRect.left) || 0) - width - GAP + offset}px` : `${((nodeRect === null || nodeRect === void 0 ? void 0 : nodeRect.right) || 0) + GAP - offset}px`,
|
|
139
197
|
top: `${(nodeRect === null || nodeRect === void 0 ? void 0 : nodeRect.top) || 0}px`,
|
|
140
198
|
width: `${width}px`,
|
|
141
199
|
height: `calc(${(anchorRectCache === null || anchorRectCache === void 0 ? void 0 : anchorRectCache.getHeight(anchorName)) || 0}px - ${HOVER_ZONE_HEIGHT_OFFSET}px)`
|
|
142
200
|
});
|
|
143
|
-
}, [anchorName, anchorRectCache, editorWith, position]);
|
|
201
|
+
}, [anchorName, anchorRectCache, editorWith, offsets, position]);
|
|
144
202
|
return jsx("div", {
|
|
145
203
|
ref: ref,
|
|
204
|
+
"data-test-id": `drop-target-hover-zone-${position}`,
|
|
146
205
|
css: [hoverZoneCommonStyle, inlineHoverZoneRectStyle]
|
|
147
206
|
});
|
|
148
207
|
};
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { isEmptyParagraph } from '@atlaskit/editor-common/utils';
|
|
2
2
|
import { MAX_LAYOUT_COLUMN_SUPPORTED } from '../consts';
|
|
3
3
|
import { isPreRelease1 } from './advanced-layouts-flags';
|
|
4
|
+
import { isWrappedMedia } from './check-media-layout';
|
|
4
5
|
export const shouldAllowInlineDropTarget = (isNested, node) => {
|
|
5
6
|
if (!isPreRelease1() || isNested) {
|
|
6
7
|
return false;
|
|
7
8
|
}
|
|
9
|
+
if (isWrappedMedia(node)) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
8
12
|
if ((node === null || node === void 0 ? void 0 : node.type.name) === 'layoutSection') {
|
|
9
13
|
return node.childCount < MAX_LAYOUT_COLUMN_SUPPORTED;
|
|
10
14
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Fragment } from '@atlaskit/editor-prosemirror/model';
|
|
2
|
+
import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
|
|
2
3
|
import { MAX_LAYOUT_COLUMN_SUPPORTED } from '../consts';
|
|
3
4
|
import { DEFAULT_COLUMN_DISTRIBUTIONS } from '../ui/consts';
|
|
4
5
|
var createNewLayout = function createNewLayout(schema, layoutContents) {
|
|
@@ -68,13 +69,25 @@ export var moveToLayout = function moveToLayout(api) {
|
|
|
68
69
|
if ($to.nodeAfter.type === layoutSection) {
|
|
69
70
|
var existingLayoutNode = $to.nodeAfter;
|
|
70
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
|
+
}
|
|
71
80
|
var toPos = position === 'left' ? to + 1 : to + existingLayoutNode.nodeSize - 1;
|
|
72
81
|
tr = tr.insert(toPos,
|
|
73
82
|
// resolve again the source node after node updated (remove breakout marks)
|
|
74
|
-
layoutColumn.create(
|
|
83
|
+
layoutColumn.create({
|
|
84
|
+
width: newColumnWidth
|
|
85
|
+
}, tr.doc.resolve(from).nodeAfter));
|
|
86
|
+
tr = tr.setSelection(new NodeSelection(tr.doc.resolve(toPos)));
|
|
75
87
|
var mappedFrom = tr.mapping.map(from);
|
|
76
88
|
var mappedFromEnd = mappedFrom + fromNode.nodeSize;
|
|
77
89
|
tr = tr.delete(mappedFrom, mappedFromEnd);
|
|
90
|
+
tr = tr.scrollIntoView();
|
|
78
91
|
return tr;
|
|
79
92
|
}
|
|
80
93
|
return tr;
|
|
@@ -91,6 +104,8 @@ export var moveToLayout = function moveToLayout(api) {
|
|
|
91
104
|
var mappedTo = tr.mapping.map(to);
|
|
92
105
|
tr = tr.delete(mappedTo, mappedTo + toNode.nodeSize);
|
|
93
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();
|
|
94
109
|
}
|
|
95
110
|
return tr;
|
|
96
111
|
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
3
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
4
|
+
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
5
|
+
import { getNestedDepth, getNodeAnchor, TYPE_NODE_DEC } from './decorations-common';
|
|
6
|
+
var IGNORE_NODES = ['tableCell', 'tableHeader', 'tableRow', 'layoutColumn', 'listItem', 'caption'];
|
|
7
|
+
var IGNORE_NODE_DESCENDANTS = ['listItem', 'taskList', 'decisionList', 'mediaSingle'];
|
|
8
|
+
export var shouldDescendIntoNode = function shouldDescendIntoNode(node) {
|
|
9
|
+
// Optimisation to avoid drawing node decorations for empty table cells
|
|
10
|
+
if (['tableCell', 'tableHeader'].includes(node.type.name) && !editorExperiment('table-nested-dnd', true) && fg('platform_editor_element_dnd_nested_fix_patch_3')) {
|
|
11
|
+
var _node$firstChild;
|
|
12
|
+
if (node.childCount === 1 && ((_node$firstChild = node.firstChild) === null || _node$firstChild === void 0 ? void 0 : _node$firstChild.type.name) === 'paragraph') {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return !IGNORE_NODE_DESCENDANTS.includes(node.type.name);
|
|
17
|
+
};
|
|
18
|
+
var shouldIgnoreNode = function shouldIgnoreNode(node) {
|
|
19
|
+
// TODO use isWrappedMedia when clean up the featue flag
|
|
20
|
+
if ('mediaSingle' === node.type.name && fg('platform_editor_element_dnd_nested_fix_patch_1')) {
|
|
21
|
+
if (['wrap-right', 'wrap-left'].includes(node.attrs.layout)) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return IGNORE_NODES.includes(node.type.name);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Find node decorations in the pos range between from and to (non-inclusive)
|
|
30
|
+
* @param decorations
|
|
31
|
+
* @param from
|
|
32
|
+
* @param to
|
|
33
|
+
* @returns
|
|
34
|
+
*/
|
|
35
|
+
export var findNodeDecs = function findNodeDecs(decorations, from, to) {
|
|
36
|
+
var newfrom = from;
|
|
37
|
+
var newTo = to;
|
|
38
|
+
|
|
39
|
+
// make it non-inclusive
|
|
40
|
+
if (newfrom !== undefined) {
|
|
41
|
+
newfrom++;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// make it non-inclusive
|
|
45
|
+
if (newTo !== undefined) {
|
|
46
|
+
newTo--;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// return empty array if range reversed
|
|
50
|
+
if (newfrom !== undefined && newTo !== undefined && newfrom > newTo) {
|
|
51
|
+
return new Array();
|
|
52
|
+
}
|
|
53
|
+
return decorations.find(newfrom, newTo, function (spec) {
|
|
54
|
+
return spec.type === TYPE_NODE_DEC;
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
export var nodeDecorations = function nodeDecorations(newState, from, to) {
|
|
58
|
+
var decs = [];
|
|
59
|
+
var docFrom = from === undefined || from < 0 ? 0 : from;
|
|
60
|
+
var docTo = to === undefined || to > newState.doc.nodeSize - 2 ? newState.doc.nodeSize - 2 : to;
|
|
61
|
+
newState.doc.nodesBetween(docFrom, docTo, function (node, pos, _parent, index) {
|
|
62
|
+
var _Decoration$node;
|
|
63
|
+
var depth = 0;
|
|
64
|
+
var anchorName;
|
|
65
|
+
var shouldDescend = shouldDescendIntoNode(node);
|
|
66
|
+
anchorName = getNodeAnchor(node);
|
|
67
|
+
if (editorExperiment('nested-dnd', true)) {
|
|
68
|
+
var _anchorName;
|
|
69
|
+
// Doesn't descend into a node
|
|
70
|
+
if (node.isInline) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
if (shouldIgnoreNode(node)) {
|
|
74
|
+
return shouldDescend; //skip over, don't consider it a valid depth
|
|
75
|
+
}
|
|
76
|
+
depth = newState.doc.resolve(pos).depth;
|
|
77
|
+
anchorName = (_anchorName = anchorName) !== null && _anchorName !== void 0 ? _anchorName : "--node-anchor-".concat(node.type.name, "-").concat(pos);
|
|
78
|
+
} else {
|
|
79
|
+
var _anchorName2;
|
|
80
|
+
anchorName = (_anchorName2 = anchorName) !== null && _anchorName2 !== void 0 ? _anchorName2 : "--node-anchor-".concat(node.type.name, "-").concat(index);
|
|
81
|
+
}
|
|
82
|
+
decs.push(Decoration.node(pos, pos + node.nodeSize, (_Decoration$node = {
|
|
83
|
+
style: "anchor-name: ".concat(anchorName, "; ").concat(pos === 0 ? 'margin-top: 0px;' : '', "; position: relative; z-index: 1;")
|
|
84
|
+
}, _defineProperty(_Decoration$node, 'data-drag-handler-anchor-name', anchorName), _defineProperty(_Decoration$node, 'data-drag-handler-node-type', node.type.name), _defineProperty(_Decoration$node, 'data-drag-handler-anchor-depth', "".concat(depth)), _Decoration$node), {
|
|
85
|
+
type: TYPE_NODE_DEC,
|
|
86
|
+
anchorName: anchorName,
|
|
87
|
+
nodeType: node.type.name
|
|
88
|
+
}));
|
|
89
|
+
return shouldDescend && depth < getNestedDepth();
|
|
90
|
+
});
|
|
91
|
+
return decs;
|
|
92
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
2
|
+
import _createClass from "@babel/runtime/helpers/createClass";
|
|
3
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
4
|
+
import ReactDOM from 'react-dom';
|
|
5
|
+
import uuid from 'uuid';
|
|
6
|
+
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
7
|
+
export var TYPE_DROP_TARGET_DEC = 'drop-target-decoration';
|
|
8
|
+
export var TYPE_HANDLE_DEC = 'drag-handle';
|
|
9
|
+
export var TYPE_NODE_DEC = 'node-decoration';
|
|
10
|
+
export var getNestedDepth = function getNestedDepth() {
|
|
11
|
+
return editorExperiment('nested-dnd', true) ? 100 : 0;
|
|
12
|
+
};
|
|
13
|
+
export var getNodeAnchor = function getNodeAnchor(node) {
|
|
14
|
+
var handleId = ObjHash.getForNode(node);
|
|
15
|
+
return "--node-anchor-".concat(node.type.name, "-").concat(handleId);
|
|
16
|
+
};
|
|
17
|
+
var ObjHash = /*#__PURE__*/function () {
|
|
18
|
+
function ObjHash() {
|
|
19
|
+
_classCallCheck(this, ObjHash);
|
|
20
|
+
}
|
|
21
|
+
_createClass(ObjHash, null, [{
|
|
22
|
+
key: "getForNode",
|
|
23
|
+
value: function getForNode(node) {
|
|
24
|
+
if (this.caching.has(node)) {
|
|
25
|
+
return this.caching.get(node);
|
|
26
|
+
}
|
|
27
|
+
var uniqueId = uuid();
|
|
28
|
+
this.caching.set(node, uniqueId);
|
|
29
|
+
return uniqueId;
|
|
30
|
+
}
|
|
31
|
+
}]);
|
|
32
|
+
return ObjHash;
|
|
33
|
+
}();
|
|
34
|
+
_defineProperty(ObjHash, "caching", new WeakMap());
|
|
35
|
+
export var unmountDecorations = function unmountDecorations(selector) {
|
|
36
|
+
// Removing decorations manually instead of using native destroy function in prosemirror API
|
|
37
|
+
// as it was more responsive and causes less re-rendering
|
|
38
|
+
var decorationsToRemove = document.querySelectorAll("[".concat(selector, "=\"true\"]"));
|
|
39
|
+
decorationsToRemove.forEach(function (el) {
|
|
40
|
+
ReactDOM.unmountComponentAtNode(el);
|
|
41
|
+
});
|
|
42
|
+
};
|