@atlaskit/editor-plugin-block-controls 2.2.2 → 2.3.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 +16 -0
- package/dist/cjs/commands/move-node.js +3 -1
- package/dist/cjs/pm-plugins/decorations.js +127 -43
- package/dist/cjs/pm-plugins/main.js +8 -1
- package/dist/cjs/ui/consts.js +3 -3
- package/dist/cjs/ui/drop-target-v2.js +184 -0
- package/dist/cjs/utils/anchor-utils.js +70 -0
- package/dist/cjs/utils/getNestedNodePosition.js +2 -2
- package/dist/es2019/commands/move-node.js +3 -1
- package/dist/es2019/pm-plugins/decorations.js +122 -33
- package/dist/es2019/pm-plugins/main.js +8 -1
- package/dist/es2019/ui/consts.js +3 -3
- package/dist/es2019/ui/drop-target-v2.js +171 -0
- package/dist/es2019/utils/anchor-utils.js +51 -0
- package/dist/es2019/utils/getNestedNodePosition.js +2 -2
- package/dist/esm/commands/move-node.js +3 -1
- package/dist/esm/pm-plugins/decorations.js +126 -42
- package/dist/esm/pm-plugins/main.js +8 -1
- package/dist/esm/ui/consts.js +3 -3
- package/dist/esm/ui/drop-target-v2.js +176 -0
- package/dist/esm/utils/anchor-utils.js +63 -0
- package/dist/esm/utils/getNestedNodePosition.js +2 -2
- package/dist/types/pm-plugins/decorations.d.ts +6 -1
- package/dist/types/ui/drop-target-v2.d.ts +8 -0
- package/dist/types/utils/anchor-utils.d.ts +12 -0
- package/dist/types-ts4.5/pm-plugins/decorations.d.ts +6 -1
- package/dist/types-ts4.5/ui/drop-target-v2.d.ts +8 -0
- package/dist/types-ts4.5/utils/anchor-utils.d.ts +12 -0
- package/package.json +4 -1
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
2
2
|
import _createClass from "@babel/runtime/helpers/createClass";
|
|
3
3
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
4
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
5
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
6
|
import { createElement } from 'react';
|
|
5
7
|
import { bind } from 'bind-event-listener';
|
|
6
8
|
import ReactDOM from 'react-dom';
|
|
@@ -10,8 +12,10 @@ import { isEmptyParagraph } from '@atlaskit/editor-common/utils';
|
|
|
10
12
|
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
11
13
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
12
14
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
15
|
+
import { nodeMargins } from '../ui/consts';
|
|
13
16
|
import { DragHandle } from '../ui/drag-handle';
|
|
14
17
|
import { DropTarget } from '../ui/drop-target';
|
|
18
|
+
import { DropTargetV2, EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_GAP, EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_OFFSET } from '../ui/drop-target-v2';
|
|
15
19
|
import { isBlocksDragTargetDebug } from '../utils/drag-target-debug';
|
|
16
20
|
import { canMoveNodeToIndex } from '../utils/validation';
|
|
17
21
|
var IGNORE_NODES = ['tableCell', 'tableHeader', 'tableRow', 'layoutColumn', 'listItem', 'caption'];
|
|
@@ -21,24 +25,101 @@ var DISABLE_CHILD_DROP_TARGET = ['orderedList', 'bulletList'];
|
|
|
21
25
|
var getNestedDepth = function getNestedDepth() {
|
|
22
26
|
return editorExperiment('nested-dnd', true) ? 100 : 0;
|
|
23
27
|
};
|
|
24
|
-
var
|
|
28
|
+
export var getNodeAnchor = function getNodeAnchor(node) {
|
|
29
|
+
var handleId = ObjHash.getForNode(node);
|
|
30
|
+
return "--node-anchor-".concat(node.type.name, "-").concat(handleId);
|
|
31
|
+
};
|
|
32
|
+
var getNodeMargins = function getNodeMargins(node) {
|
|
33
|
+
if (!node) {
|
|
34
|
+
return nodeMargins['default'];
|
|
35
|
+
}
|
|
36
|
+
var nodeTypeName = node.type.name;
|
|
37
|
+
if (nodeTypeName === 'heading') {
|
|
38
|
+
return nodeMargins["heading".concat(node.attrs.level)] || nodeMargins['default'];
|
|
39
|
+
}
|
|
40
|
+
return nodeMargins[nodeTypeName] || nodeMargins['default'];
|
|
41
|
+
};
|
|
42
|
+
var getGapAndOffset = function getGapAndOffset(prevNode, nextNode, parentNode) {
|
|
43
|
+
if (!prevNode && nextNode) {
|
|
44
|
+
// first node
|
|
45
|
+
return {
|
|
46
|
+
gap: 0,
|
|
47
|
+
offset: 0
|
|
48
|
+
};
|
|
49
|
+
} else if (prevNode && !nextNode) {
|
|
50
|
+
return {
|
|
51
|
+
gap: 0,
|
|
52
|
+
offset: 0
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
var top = getNodeMargins(nextNode).top || 4;
|
|
56
|
+
var bottom = getNodeMargins(prevNode).bottom || 4;
|
|
57
|
+
var gap = Math.max(top, bottom);
|
|
58
|
+
var offset = top - gap / 2;
|
|
59
|
+
if ((prevNode === null || prevNode === void 0 ? void 0 : prevNode.type.name) === 'mediaSingle' && (nextNode === null || nextNode === void 0 ? void 0 : nextNode.type.name) === 'mediaSingle') {
|
|
60
|
+
offset = -offset;
|
|
61
|
+
} else if (prevNode !== null && prevNode !== void 0 && prevNode.type.name && ['tableCell', 'tableHeader'].includes(prevNode === null || prevNode === void 0 ? void 0 : prevNode.type.name)) {
|
|
62
|
+
offset = 0;
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
gap: gap,
|
|
66
|
+
offset: offset
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
var shouldDescend = function shouldDescend(node) {
|
|
70
|
+
if (fg('platform_editor_drag_and_drop_target_v2')) {
|
|
71
|
+
return !['mediaSingle', 'paragraph', 'heading'].includes(node.type.name);
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
};
|
|
75
|
+
export var createDropTargetDecoration = function createDropTargetDecoration(pos, props, side, anchorHeightsCache) {
|
|
25
76
|
return Decoration.widget(pos, function (_, getPos) {
|
|
26
77
|
var element = document.createElement('div');
|
|
27
78
|
element.setAttribute('data-blocks-drop-target-container', 'true');
|
|
28
79
|
element.style.clear = 'unset';
|
|
29
|
-
|
|
80
|
+
if (fg('platform_editor_drag_and_drop_target_v2')) {
|
|
81
|
+
var _getGapAndOffset = getGapAndOffset(props.prevNode, props.nextNode, props.parentNode),
|
|
82
|
+
gap = _getGapAndOffset.gap,
|
|
83
|
+
offset = _getGapAndOffset.offset;
|
|
84
|
+
element.style.setProperty(EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_OFFSET, "".concat(offset, "px"));
|
|
85
|
+
element.style.setProperty(EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_GAP, "".concat(gap, "px"));
|
|
86
|
+
}
|
|
87
|
+
if (fg('platform_editor_drag_and_drop_target_v2')) {
|
|
88
|
+
ReactDOM.render( /*#__PURE__*/createElement(DropTargetV2, _objectSpread(_objectSpread({}, props), {}, {
|
|
89
|
+
getPos: getPos,
|
|
90
|
+
anchorHeightsCache: anchorHeightsCache
|
|
91
|
+
})), element);
|
|
92
|
+
} else {
|
|
93
|
+
ReactDOM.render( /*#__PURE__*/createElement(DropTarget, _objectSpread(_objectSpread({}, props), {}, {
|
|
94
|
+
getPos: getPos
|
|
95
|
+
})), element);
|
|
96
|
+
}
|
|
30
97
|
return element;
|
|
31
98
|
}, {
|
|
32
99
|
type: 'drop-target-decoration',
|
|
33
|
-
side:
|
|
100
|
+
side: side
|
|
34
101
|
});
|
|
35
102
|
};
|
|
36
|
-
export var dropTargetDecorations = function dropTargetDecorations(newState, api, formatMessage, activeNode) {
|
|
103
|
+
export var dropTargetDecorations = function dropTargetDecorations(newState, api, formatMessage, activeNode, anchorHeightsCache) {
|
|
37
104
|
var decs = [];
|
|
38
105
|
unmountDecorations('data-blocks-drop-target-container');
|
|
39
106
|
var prevNode;
|
|
40
107
|
var activeNodePos = activeNode === null || activeNode === void 0 ? void 0 : activeNode.pos;
|
|
41
108
|
var activePMNode = typeof activeNodePos === 'number' && newState.doc.resolve(activeNodePos).nodeAfter;
|
|
109
|
+
anchorHeightsCache === null || anchorHeightsCache === void 0 || anchorHeightsCache.clear();
|
|
110
|
+
var prevNodeStack = [];
|
|
111
|
+
var popNodeStack = function popNodeStack(depth) {
|
|
112
|
+
var result;
|
|
113
|
+
var toDepth = Math.max(depth, 0);
|
|
114
|
+
while (prevNodeStack.length > toDepth) {
|
|
115
|
+
result = prevNodeStack.pop();
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
};
|
|
119
|
+
var pushNodeStack = function pushNodeStack(node, depth) {
|
|
120
|
+
popNodeStack(depth);
|
|
121
|
+
prevNodeStack.push(node);
|
|
122
|
+
};
|
|
42
123
|
newState.doc.descendants(function (node, pos, parent, index) {
|
|
43
124
|
var depth = 0;
|
|
44
125
|
// drop target deco at the end position
|
|
@@ -46,62 +127,65 @@ export var dropTargetDecorations = function dropTargetDecorations(newState, api,
|
|
|
46
127
|
if (editorExperiment('nested-dnd', true)) {
|
|
47
128
|
depth = newState.doc.resolve(pos).depth;
|
|
48
129
|
if (node.isInline || !parent || DISABLE_CHILD_DROP_TARGET.includes(parent.type.name)) {
|
|
49
|
-
|
|
130
|
+
if (fg('platform_editor_drag_and_drop_target_v2')) {
|
|
131
|
+
pushNodeStack(node, depth);
|
|
132
|
+
} else {
|
|
133
|
+
prevNode = node;
|
|
134
|
+
}
|
|
50
135
|
return false;
|
|
51
136
|
}
|
|
52
137
|
if (IGNORE_NODES.includes(node.type.name)) {
|
|
53
|
-
|
|
54
|
-
|
|
138
|
+
if (fg('platform_editor_drag_and_drop_target_v2')) {
|
|
139
|
+
pushNodeStack(node, depth);
|
|
140
|
+
} else {
|
|
141
|
+
prevNode = node;
|
|
142
|
+
}
|
|
143
|
+
return shouldDescend(node); //skip over, don't consider it a valid depth
|
|
55
144
|
}
|
|
56
145
|
var canDrop = activePMNode && canMoveNodeToIndex(parent, index, activePMNode);
|
|
57
146
|
|
|
58
147
|
//NOTE: This will block drop targets showing for nodes that are valid after transformation (i.e. expand -> nestedExpand)
|
|
59
148
|
if (!canDrop && !isBlocksDragTargetDebug()) {
|
|
60
|
-
|
|
149
|
+
if (fg('platform_editor_drag_and_drop_target_v2')) {
|
|
150
|
+
pushNodeStack(node, depth);
|
|
151
|
+
} else {
|
|
152
|
+
prevNode = node;
|
|
153
|
+
}
|
|
61
154
|
return false; //not valid pos, so nested not valid either
|
|
62
155
|
}
|
|
63
156
|
if (parent.lastChild === node && !isEmptyParagraph(node) && PARENT_WITH_END_DROP_TARGET.includes(parent.type.name)) {
|
|
64
157
|
endPos = pos + node.nodeSize;
|
|
65
158
|
}
|
|
66
159
|
}
|
|
67
|
-
var previousNode = prevNode; // created scoped variable
|
|
68
|
-
decs.push(createDropTargetDecoration(pos,
|
|
69
|
-
|
|
160
|
+
var previousNode = fg('platform_editor_drag_and_drop_target_v2') ? popNodeStack(depth) : prevNode; // created scoped variable
|
|
161
|
+
decs.push(createDropTargetDecoration(pos, {
|
|
162
|
+
api: api,
|
|
163
|
+
prevNode: previousNode,
|
|
164
|
+
nextNode: node,
|
|
165
|
+
parentNode: parent || undefined,
|
|
166
|
+
formatMessage: formatMessage
|
|
167
|
+
}, -1, anchorHeightsCache));
|
|
168
|
+
if (endPos !== undefined) {
|
|
169
|
+
decs.push(createDropTargetDecoration(endPos, {
|
|
70
170
|
api: api,
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
nextNode: node,
|
|
74
|
-
parentNode: parent,
|
|
171
|
+
prevNode: fg('platform_editor_drag_and_drop_target_v2') ? node : undefined,
|
|
172
|
+
parentNode: parent || undefined,
|
|
75
173
|
formatMessage: formatMessage
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
getPos: getPos,
|
|
83
|
-
parentNode: parent,
|
|
84
|
-
formatMessage: formatMessage
|
|
85
|
-
});
|
|
86
|
-
}));
|
|
174
|
+
}, -1, anchorHeightsCache));
|
|
175
|
+
}
|
|
176
|
+
if (fg('platform_editor_drag_and_drop_target_v2')) {
|
|
177
|
+
pushNodeStack(node, depth);
|
|
178
|
+
} else {
|
|
179
|
+
prevNode = node;
|
|
87
180
|
}
|
|
88
|
-
|
|
89
|
-
return depth < getNestedDepth();
|
|
181
|
+
return depth < getNestedDepth() && shouldDescend(node);
|
|
90
182
|
});
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
api: api,
|
|
98
|
-
getPos: getPos,
|
|
99
|
-
formatMessage: formatMessage
|
|
100
|
-
}), element);
|
|
101
|
-
return element;
|
|
102
|
-
}, {
|
|
103
|
-
type: 'drop-target-decoration'
|
|
104
|
-
}));
|
|
183
|
+
decs.push(createDropTargetDecoration(newState.doc.nodeSize - 2, {
|
|
184
|
+
api: api,
|
|
185
|
+
formatMessage: formatMessage,
|
|
186
|
+
prevNode: newState.doc.lastChild || undefined,
|
|
187
|
+
parentNode: newState.doc
|
|
188
|
+
}, undefined, anchorHeightsCache));
|
|
105
189
|
return decs;
|
|
106
190
|
};
|
|
107
191
|
export var emptyParagraphNodeDecorations = function emptyParagraphNodeDecorations() {
|
|
@@ -12,6 +12,7 @@ import { autoScrollForElements } from '@atlaskit/pragmatic-drag-and-drop-auto-sc
|
|
|
12
12
|
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
|
|
13
13
|
import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
14
14
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
15
|
+
import { AnchorHeightsCache, isAnchorSupported } from '../utils/anchor-utils';
|
|
15
16
|
import { isBlocksDragTargetDebug } from '../utils/drag-target-debug';
|
|
16
17
|
import { dragHandleDecoration, dropTargetDecorations, emptyParagraphNodeDecorations, nodeDecorations } from './decorations';
|
|
17
18
|
import { handleMouseOver } from './handle-mouse-over';
|
|
@@ -95,6 +96,10 @@ export var createPlugin = function createPlugin(api, getIntl) {
|
|
|
95
96
|
if (fg('platform_editor_element_dnd_nested_fix_patch_2')) {
|
|
96
97
|
// TODO: Remove this once FG is used in code
|
|
97
98
|
}
|
|
99
|
+
var anchorHeightsCache;
|
|
100
|
+
if (!isAnchorSupported() && fg('platform_editor_drag_and_drop_target_v2')) {
|
|
101
|
+
anchorHeightsCache = new AnchorHeightsCache();
|
|
102
|
+
}
|
|
98
103
|
return new SafePlugin({
|
|
99
104
|
key: key,
|
|
100
105
|
state: {
|
|
@@ -263,7 +268,7 @@ export var createPlugin = function createPlugin(api, getIntl) {
|
|
|
263
268
|
// if the transaction is only for analytics and user is dragging, continue to draw drop targets
|
|
264
269
|
if (shouldCreateDropTargets || isBlocksDragTargetDebug()) {
|
|
265
270
|
var _meta$activeNode6;
|
|
266
|
-
var _decs2 = 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);
|
|
271
|
+
var _decs2 = 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);
|
|
267
272
|
decorations = decorations.add(newState.doc, _decs2);
|
|
268
273
|
}
|
|
269
274
|
}
|
|
@@ -340,6 +345,8 @@ export var createPlugin = function createPlugin(api, getIntl) {
|
|
|
340
345
|
return false;
|
|
341
346
|
},
|
|
342
347
|
dragstart: function dragstart(view) {
|
|
348
|
+
var _anchorHeightsCache;
|
|
349
|
+
(_anchorHeightsCache = anchorHeightsCache) === null || _anchorHeightsCache === void 0 || _anchorHeightsCache.setEditorView(view);
|
|
343
350
|
view.dispatch(view.state.tr.setMeta(key, {
|
|
344
351
|
isPMDragging: true
|
|
345
352
|
}));
|
package/dist/esm/ui/consts.js
CHANGED
|
@@ -100,7 +100,7 @@ export var nodeMargins = {
|
|
|
100
100
|
bottom: 0
|
|
101
101
|
},
|
|
102
102
|
codeBlock: {
|
|
103
|
-
top:
|
|
103
|
+
top: 12,
|
|
104
104
|
bottom: 0
|
|
105
105
|
},
|
|
106
106
|
panel: {
|
|
@@ -108,8 +108,8 @@ export var nodeMargins = {
|
|
|
108
108
|
bottom: 0
|
|
109
109
|
},
|
|
110
110
|
rule: {
|
|
111
|
-
top:
|
|
112
|
-
bottom:
|
|
111
|
+
top: 24,
|
|
112
|
+
bottom: 24
|
|
113
113
|
},
|
|
114
114
|
mediaSingle: {
|
|
115
115
|
top: 24,
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
3
|
+
/**
|
|
4
|
+
* @jsxRuntime classic
|
|
5
|
+
* @jsx jsx
|
|
6
|
+
*/
|
|
7
|
+
import { Fragment, useEffect, useMemo, useRef, useState } from 'react';
|
|
8
|
+
|
|
9
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
|
|
10
|
+
import { css, jsx } from '@emotion/react';
|
|
11
|
+
import { useSharedPluginState } from '@atlaskit/editor-common/hooks';
|
|
12
|
+
import { DropIndicator } from '@atlaskit/pragmatic-drag-and-drop-react-drop-indicator/box';
|
|
13
|
+
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
14
|
+
import { layers } from '@atlaskit/theme/constants';
|
|
15
|
+
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
16
|
+
import { getNodeAnchor } from '../pm-plugins/decorations';
|
|
17
|
+
import { isAnchorSupported } from '../utils/anchor-utils';
|
|
18
|
+
import { isBlocksDragTargetDebug } from '../utils/drag-target-debug';
|
|
19
|
+
import { getNestedNodeLeftPaddingMargin } from './consts';
|
|
20
|
+
var DEFAULT_DROP_INDICATOR_WIDTH = 760;
|
|
21
|
+
var EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_WIDTH = '--editor-block-controls-drop-indicator-width';
|
|
22
|
+
var EDITOR_BLOCK_CONTROLS_DROP_TARGET_LEFT_MARGIN = '--editor-block-controls-drop-target-leftMargin';
|
|
23
|
+
var EDITOR_BLOCK_CONTROLS_DROP_TARGET_ZINDEX = '--editor-block-controls-drop-target-zindex';
|
|
24
|
+
export var EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_OFFSET = '--editor-block-controls-drop-indicator-offset';
|
|
25
|
+
export var EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_GAP = '--editor-block-controls-drop-indicator-gap';
|
|
26
|
+
var styleDropTarget = css({
|
|
27
|
+
marginLeft: "calc(-1 * var(".concat(EDITOR_BLOCK_CONTROLS_DROP_TARGET_LEFT_MARGIN, ", 0))"),
|
|
28
|
+
paddingLeft: "var(".concat(EDITOR_BLOCK_CONTROLS_DROP_TARGET_LEFT_MARGIN, ", 0)"),
|
|
29
|
+
position: 'absolute',
|
|
30
|
+
left: '0',
|
|
31
|
+
display: 'block',
|
|
32
|
+
zIndex: "var(".concat(EDITOR_BLOCK_CONTROLS_DROP_TARGET_ZINDEX, ", 110)"),
|
|
33
|
+
transform: "translateY(var(".concat(EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_OFFSET, ", 0))")
|
|
34
|
+
});
|
|
35
|
+
var styleDropIndicator = css({
|
|
36
|
+
height: '100%',
|
|
37
|
+
margin: '0 auto',
|
|
38
|
+
position: 'relative',
|
|
39
|
+
width: "var(".concat(EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_WIDTH, ", 100%)")
|
|
40
|
+
});
|
|
41
|
+
var nestedDropIndicatorStyle = css({
|
|
42
|
+
position: 'relative'
|
|
43
|
+
});
|
|
44
|
+
var dropZoneStyles = css({
|
|
45
|
+
margin: 0,
|
|
46
|
+
position: 'absolute',
|
|
47
|
+
width: '100%',
|
|
48
|
+
zIndex: 110,
|
|
49
|
+
minHeight: '4px'
|
|
50
|
+
});
|
|
51
|
+
var nestedDropZoneStyle = css({
|
|
52
|
+
left: '4px',
|
|
53
|
+
right: '4px',
|
|
54
|
+
width: 'unset'
|
|
55
|
+
});
|
|
56
|
+
var enableDropZone = ['paragraph', 'mediaSingle', 'heading', 'codeBlock', 'decisionList', 'bulletList', 'orderedList', 'taskList', 'extension', 'blockCard'];
|
|
57
|
+
var HoverZone = function HoverZone(_ref) {
|
|
58
|
+
var onDragEnter = _ref.onDragEnter,
|
|
59
|
+
onDragLeave = _ref.onDragLeave,
|
|
60
|
+
onDrop = _ref.onDrop,
|
|
61
|
+
node = _ref.node,
|
|
62
|
+
editorWidth = _ref.editorWidth,
|
|
63
|
+
anchorHeightsCache = _ref.anchorHeightsCache,
|
|
64
|
+
position = _ref.position,
|
|
65
|
+
isNestedDropTarget = _ref.isNestedDropTarget;
|
|
66
|
+
var ref = useRef(null);
|
|
67
|
+
useEffect(function () {
|
|
68
|
+
if (ref.current) {
|
|
69
|
+
return dropTargetForElements({
|
|
70
|
+
element: ref.current,
|
|
71
|
+
onDragEnter: onDragEnter,
|
|
72
|
+
onDragLeave: onDragLeave,
|
|
73
|
+
onDrop: onDrop
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}, [onDragEnter, onDragLeave, onDrop]);
|
|
77
|
+
var hoverZoneUpperStyle = useMemo(function () {
|
|
78
|
+
var anchorName = node ? getNodeAnchor(node) : '';
|
|
79
|
+
var heightStyleOffset = "var(--editor-block-controls-drop-indicator-gap, 0)/2";
|
|
80
|
+
var transformOffset = "var(".concat(EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_OFFSET, ", 0)");
|
|
81
|
+
var heightStyle = anchorName && enableDropZone.includes((node === null || node === void 0 ? void 0 : node.type.name) || '') ? isAnchorSupported() ? "calc(anchor-size(".concat(anchorName, " height)/2 + ").concat(heightStyleOffset, ")") : "calc(".concat(((anchorHeightsCache === null || anchorHeightsCache === void 0 ? void 0 : anchorHeightsCache.getHeight(anchorName)) || 0) / 2, "px + ").concat(heightStyleOffset, ")") : '4px';
|
|
82
|
+
var transform = position === 'upper' ? "translateY(calc(-100% + ".concat(transformOffset, "))") : "translateY(".concat(transformOffset, ")");
|
|
83
|
+
return css({
|
|
84
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values
|
|
85
|
+
height: heightStyle,
|
|
86
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values
|
|
87
|
+
transform: transform,
|
|
88
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values
|
|
89
|
+
maxWidth: "".concat(editorWidth || 0, "px")
|
|
90
|
+
});
|
|
91
|
+
}, [anchorHeightsCache, editorWidth, node, position]);
|
|
92
|
+
return jsx("div", {
|
|
93
|
+
ref: ref
|
|
94
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
|
|
95
|
+
,
|
|
96
|
+
className: "drop-target-hover-zone-".concat(position)
|
|
97
|
+
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
|
|
98
|
+
,
|
|
99
|
+
css: [dropZoneStyles, isNestedDropTarget && nestedDropZoneStyle, hoverZoneUpperStyle]
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
export var DropTargetV2 = function DropTargetV2(_ref2) {
|
|
103
|
+
var _dynamicStyle;
|
|
104
|
+
var api = _ref2.api,
|
|
105
|
+
getPos = _ref2.getPos,
|
|
106
|
+
prevNode = _ref2.prevNode,
|
|
107
|
+
nextNode = _ref2.nextNode,
|
|
108
|
+
parentNode = _ref2.parentNode,
|
|
109
|
+
formatMessage = _ref2.formatMessage,
|
|
110
|
+
anchorHeightsCache = _ref2.anchorHeightsCache;
|
|
111
|
+
var _useState = useState(false),
|
|
112
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
113
|
+
isDraggedOver = _useState2[0],
|
|
114
|
+
setIsDraggedOver = _useState2[1];
|
|
115
|
+
var _useSharedPluginState = useSharedPluginState(api, ['width']),
|
|
116
|
+
widthState = _useSharedPluginState.widthState;
|
|
117
|
+
var isNestedDropTarget = (parentNode === null || parentNode === void 0 ? void 0 : parentNode.type.name) !== 'doc';
|
|
118
|
+
var onDrop = function onDrop() {
|
|
119
|
+
var _api$blockControls;
|
|
120
|
+
var _ref3 = (api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.sharedState.currentState()) || {},
|
|
121
|
+
activeNode = _ref3.activeNode;
|
|
122
|
+
if (!activeNode) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
var pos = getPos();
|
|
126
|
+
if (activeNode && pos !== undefined) {
|
|
127
|
+
var _api$core, _api$blockControls2;
|
|
128
|
+
var start = activeNode.pos;
|
|
129
|
+
api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(api === null || api === void 0 || (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 || (_api$blockControls2 = _api$blockControls2.commands) === null || _api$blockControls2 === void 0 ? void 0 : _api$blockControls2.moveNode(start, pos, undefined, formatMessage));
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
var dynamicStyle = (_dynamicStyle = {
|
|
133
|
+
width: isNestedDropTarget ? 'unset' : '100%'
|
|
134
|
+
}, _defineProperty(_dynamicStyle, EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_WIDTH, isNestedDropTarget ? '100%' : "".concat((widthState === null || widthState === void 0 ? void 0 : widthState.lineLength) || DEFAULT_DROP_INDICATOR_WIDTH, "px")), _defineProperty(_dynamicStyle, EDITOR_BLOCK_CONTROLS_DROP_TARGET_LEFT_MARGIN, isNestedDropTarget ? getNestedNodeLeftPaddingMargin(parentNode === null || parentNode === void 0 ? void 0 : parentNode.type.name) : '0'), _defineProperty(_dynamicStyle, EDITOR_BLOCK_CONTROLS_DROP_TARGET_ZINDEX, editorExperiment('nested-dnd', true) ? layers.navigation() : layers.card()), _dynamicStyle);
|
|
135
|
+
return jsx(Fragment, null, jsx(HoverZone, {
|
|
136
|
+
onDragEnter: function onDragEnter() {
|
|
137
|
+
return setIsDraggedOver(true);
|
|
138
|
+
},
|
|
139
|
+
onDragLeave: function onDragLeave() {
|
|
140
|
+
return setIsDraggedOver(false);
|
|
141
|
+
},
|
|
142
|
+
onDrop: onDrop,
|
|
143
|
+
node: prevNode,
|
|
144
|
+
editorWidth: widthState === null || widthState === void 0 ? void 0 : widthState.lineLength,
|
|
145
|
+
anchorHeightsCache: anchorHeightsCache,
|
|
146
|
+
position: "upper",
|
|
147
|
+
isNestedDropTarget: isNestedDropTarget
|
|
148
|
+
}), jsx("div", {
|
|
149
|
+
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
|
|
150
|
+
css: [styleDropTarget, isNestedDropTarget && nestedDropIndicatorStyle]
|
|
151
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop
|
|
152
|
+
,
|
|
153
|
+
style: dynamicStyle,
|
|
154
|
+
"data-testid": "block-ctrl-drop-target"
|
|
155
|
+
},
|
|
156
|
+
// 4px gap to clear expand node border
|
|
157
|
+
(isDraggedOver || isBlocksDragTargetDebug()) && jsx("div", {
|
|
158
|
+
css: styleDropIndicator,
|
|
159
|
+
"data-testid": "block-ctrl-drop-indicator"
|
|
160
|
+
}, jsx(DropIndicator, {
|
|
161
|
+
edge: "bottom"
|
|
162
|
+
}))), jsx(HoverZone, {
|
|
163
|
+
onDragEnter: function onDragEnter() {
|
|
164
|
+
return setIsDraggedOver(true);
|
|
165
|
+
},
|
|
166
|
+
onDragLeave: function onDragLeave() {
|
|
167
|
+
return setIsDraggedOver(false);
|
|
168
|
+
},
|
|
169
|
+
onDrop: onDrop,
|
|
170
|
+
node: nextNode,
|
|
171
|
+
editorWidth: widthState === null || widthState === void 0 ? void 0 : widthState.lineLength,
|
|
172
|
+
anchorHeightsCache: anchorHeightsCache,
|
|
173
|
+
position: "lower",
|
|
174
|
+
isNestedDropTarget: isNestedDropTarget
|
|
175
|
+
}));
|
|
176
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
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
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
5
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
6
|
+
import memoizeOne from 'memoize-one';
|
|
7
|
+
export var isAnchorSupported = memoizeOne(function () {
|
|
8
|
+
// directly use CSS would cause failed SSR tests.
|
|
9
|
+
if (window.CSS && window.CSS.supports) {
|
|
10
|
+
return window.CSS.supports('anchor-name: --a');
|
|
11
|
+
}
|
|
12
|
+
return false;
|
|
13
|
+
});
|
|
14
|
+
export var AnchorHeightsCache = /*#__PURE__*/function () {
|
|
15
|
+
function AnchorHeightsCache() {
|
|
16
|
+
_classCallCheck(this, AnchorHeightsCache);
|
|
17
|
+
_defineProperty(this, "anchorHeightsMap", {});
|
|
18
|
+
_defineProperty(this, "isAnchorSupported", isAnchorSupported());
|
|
19
|
+
_defineProperty(this, "isDirty", true);
|
|
20
|
+
_defineProperty(this, "view", null);
|
|
21
|
+
}
|
|
22
|
+
_createClass(AnchorHeightsCache, [{
|
|
23
|
+
key: "clear",
|
|
24
|
+
value: function clear() {
|
|
25
|
+
this.isDirty = true;
|
|
26
|
+
this.anchorHeightsMap = {};
|
|
27
|
+
}
|
|
28
|
+
}, {
|
|
29
|
+
key: "getHeights",
|
|
30
|
+
value: function getHeights() {
|
|
31
|
+
if (this.isDirty) {
|
|
32
|
+
var _this$view;
|
|
33
|
+
var anchorElements = ((_this$view = this.view) === null || _this$view === void 0 ? void 0 : _this$view.dom.querySelectorAll('[data-drag-handler-anchor-name]')) || [];
|
|
34
|
+
this.anchorHeightsMap = Array.from(anchorElements).reduce(function (prev, curr) {
|
|
35
|
+
var anchorName = curr.getAttribute('data-drag-handler-anchor-name');
|
|
36
|
+
if (anchorName) {
|
|
37
|
+
return _objectSpread(_objectSpread({}, prev), {}, _defineProperty({}, anchorName, curr.clientHeight));
|
|
38
|
+
}
|
|
39
|
+
return prev;
|
|
40
|
+
}, {});
|
|
41
|
+
this.isDirty = false;
|
|
42
|
+
}
|
|
43
|
+
return this.anchorHeightsMap;
|
|
44
|
+
}
|
|
45
|
+
}, {
|
|
46
|
+
key: "setEditorView",
|
|
47
|
+
value: function setEditorView(view) {
|
|
48
|
+
if (this.view !== view) {
|
|
49
|
+
this.view = view;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}, {
|
|
53
|
+
key: "getHeight",
|
|
54
|
+
value: function getHeight(anchorName) {
|
|
55
|
+
if (this.isAnchorSupported) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
var heights = this.getHeights();
|
|
59
|
+
return heights[anchorName];
|
|
60
|
+
}
|
|
61
|
+
}]);
|
|
62
|
+
return AnchorHeightsCache;
|
|
63
|
+
}();
|
|
@@ -9,10 +9,10 @@ export var getNestedNodePosition = function getNestedNodePosition(state) {
|
|
|
9
9
|
if ($pos.depth < 1) {
|
|
10
10
|
return nestedNodePos;
|
|
11
11
|
}
|
|
12
|
-
var parentNodeOfSpecificTypes = findParentNodeOfType([state.schema.nodes.bulletList, state.schema.nodes.orderedList, state.schema.nodes.blockquote, state.schema.nodes.taskList])(state.selection);
|
|
12
|
+
var parentNodeOfSpecificTypes = findParentNodeOfType([state.schema.nodes.bulletList, state.schema.nodes.orderedList, state.schema.nodes.blockquote, state.schema.nodes.taskList, state.schema.nodes.decisionList])(state.selection);
|
|
13
13
|
if (parentNodeOfSpecificTypes) {
|
|
14
14
|
var parentNodeType = parentNodeOfSpecificTypes.node.type.name;
|
|
15
|
-
nestedNodePos = ['bulletList', 'orderedList'].includes(parentNodeType) ? $pos.before($pos.depth - 1) : ['blockquote', 'taskList'].includes(parentNodeType) ? $pos.before() : nestedNodePos;
|
|
15
|
+
nestedNodePos = ['bulletList', 'orderedList'].includes(parentNodeType) ? $pos.before($pos.depth - 1) : ['blockquote', 'taskList', 'decisionList'].includes(parentNodeType) ? $pos.before() : nestedNodePos;
|
|
16
16
|
}
|
|
17
17
|
} else {
|
|
18
18
|
nestedNodePos = selection.$from.pos;
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { type IntlShape } from 'react-intl-next';
|
|
2
2
|
import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
3
|
+
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
3
4
|
import type { EditorState } from '@atlaskit/editor-prosemirror/state';
|
|
4
5
|
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
5
6
|
import type { ActiveNode, BlockControlsPlugin, HandleOptions } from '../types';
|
|
6
|
-
|
|
7
|
+
import { type DropTargetProps } from '../ui/drop-target';
|
|
8
|
+
import { type AnchorHeightsCache } from '../utils/anchor-utils';
|
|
9
|
+
export declare const getNodeAnchor: (node: PMNode) => string;
|
|
10
|
+
export declare const createDropTargetDecoration: (pos: number, props: Omit<DropTargetProps, 'getPos'>, side?: number, anchorHeightsCache?: AnchorHeightsCache) => Decoration;
|
|
11
|
+
export declare const dropTargetDecorations: (newState: EditorState, api: ExtractInjectionAPI<BlockControlsPlugin>, formatMessage: IntlShape['formatMessage'], activeNode?: ActiveNode, anchorHeightsCache?: AnchorHeightsCache) => Decoration[];
|
|
7
12
|
export declare const emptyParagraphNodeDecorations: () => Decoration;
|
|
8
13
|
export declare const nodeDecorations: (newState: EditorState) => Decoration[];
|
|
9
14
|
export declare const dragHandleDecoration: (api: ExtractInjectionAPI<BlockControlsPlugin>, getIntl: () => IntlShape, pos: number, anchorName: string, nodeType: string, handleOptions?: HandleOptions) => Decoration;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx } from '@emotion/react';
|
|
2
|
+
import { type AnchorHeightsCache } from '../utils/anchor-utils';
|
|
3
|
+
import { type DropTargetProps } from './drop-target';
|
|
4
|
+
export declare const EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_OFFSET = "--editor-block-controls-drop-indicator-offset";
|
|
5
|
+
export declare const EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_GAP = "--editor-block-controls-drop-indicator-gap";
|
|
6
|
+
export declare const DropTargetV2: ({ api, getPos, prevNode, nextNode, parentNode, formatMessage, anchorHeightsCache, }: DropTargetProps & {
|
|
7
|
+
anchorHeightsCache?: AnchorHeightsCache | undefined;
|
|
8
|
+
}) => jsx.JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
2
|
+
export declare const isAnchorSupported: import("memoize-one").MemoizedFn<() => boolean>;
|
|
3
|
+
export declare class AnchorHeightsCache {
|
|
4
|
+
private anchorHeightsMap;
|
|
5
|
+
private isAnchorSupported;
|
|
6
|
+
private isDirty;
|
|
7
|
+
private view;
|
|
8
|
+
clear(): void;
|
|
9
|
+
private getHeights;
|
|
10
|
+
setEditorView(view: EditorView): void;
|
|
11
|
+
getHeight(anchorName: string): number | null;
|
|
12
|
+
}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { type IntlShape } from 'react-intl-next';
|
|
2
2
|
import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
3
|
+
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
3
4
|
import type { EditorState } from '@atlaskit/editor-prosemirror/state';
|
|
4
5
|
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
5
6
|
import type { ActiveNode, BlockControlsPlugin, HandleOptions } from '../types';
|
|
6
|
-
|
|
7
|
+
import { type DropTargetProps } from '../ui/drop-target';
|
|
8
|
+
import { type AnchorHeightsCache } from '../utils/anchor-utils';
|
|
9
|
+
export declare const getNodeAnchor: (node: PMNode) => string;
|
|
10
|
+
export declare const createDropTargetDecoration: (pos: number, props: Omit<DropTargetProps, 'getPos'>, side?: number, anchorHeightsCache?: AnchorHeightsCache) => Decoration;
|
|
11
|
+
export declare const dropTargetDecorations: (newState: EditorState, api: ExtractInjectionAPI<BlockControlsPlugin>, formatMessage: IntlShape['formatMessage'], activeNode?: ActiveNode, anchorHeightsCache?: AnchorHeightsCache) => Decoration[];
|
|
7
12
|
export declare const emptyParagraphNodeDecorations: () => Decoration;
|
|
8
13
|
export declare const nodeDecorations: (newState: EditorState) => Decoration[];
|
|
9
14
|
export declare const dragHandleDecoration: (api: ExtractInjectionAPI<BlockControlsPlugin>, getIntl: () => IntlShape, pos: number, anchorName: string, nodeType: string, handleOptions?: HandleOptions) => Decoration;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx } from '@emotion/react';
|
|
2
|
+
import { type AnchorHeightsCache } from '../utils/anchor-utils';
|
|
3
|
+
import { type DropTargetProps } from './drop-target';
|
|
4
|
+
export declare const EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_OFFSET = "--editor-block-controls-drop-indicator-offset";
|
|
5
|
+
export declare const EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_GAP = "--editor-block-controls-drop-indicator-gap";
|
|
6
|
+
export declare const DropTargetV2: ({ api, getPos, prevNode, nextNode, parentNode, formatMessage, anchorHeightsCache, }: DropTargetProps & {
|
|
7
|
+
anchorHeightsCache?: AnchorHeightsCache | undefined;
|
|
8
|
+
}) => jsx.JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
2
|
+
export declare const isAnchorSupported: import("memoize-one").MemoizedFn<() => boolean>;
|
|
3
|
+
export declare class AnchorHeightsCache {
|
|
4
|
+
private anchorHeightsMap;
|
|
5
|
+
private isAnchorSupported;
|
|
6
|
+
private isDirty;
|
|
7
|
+
private view;
|
|
8
|
+
clear(): void;
|
|
9
|
+
private getHeights;
|
|
10
|
+
setEditorView(view: EditorView): void;
|
|
11
|
+
getHeight(anchorName: string): number | null;
|
|
12
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-block-controls",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"description": "Block controls plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -150,6 +150,9 @@
|
|
|
150
150
|
},
|
|
151
151
|
"platform_editor_element_dnd_nested_a11y": {
|
|
152
152
|
"type": "boolean"
|
|
153
|
+
},
|
|
154
|
+
"platform_editor_drag_and_drop_target_v2": {
|
|
155
|
+
"type": "boolean"
|
|
153
156
|
}
|
|
154
157
|
}
|
|
155
158
|
}
|