@atlaskit/editor-plugin-block-controls 2.26.1 → 2.26.2

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 (42) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/cjs/editor-commands/move-node.js +16 -15
  3. package/dist/cjs/editor-commands/move-to-layout.js +20 -8
  4. package/dist/cjs/editor-commands/show-drag-handle.js +89 -3
  5. package/dist/cjs/pm-plugins/decorations-anchor.js +5 -10
  6. package/dist/cjs/pm-plugins/decorations-common.js +5 -1
  7. package/dist/cjs/pm-plugins/main.js +28 -8
  8. package/dist/cjs/pm-plugins/utils/analytics.js +66 -0
  9. package/dist/cjs/pm-plugins/utils/selection.js +22 -2
  10. package/dist/cjs/ui/drag-handle.js +23 -9
  11. package/dist/es2019/editor-commands/move-node.js +17 -16
  12. package/dist/es2019/editor-commands/move-to-layout.js +20 -8
  13. package/dist/es2019/editor-commands/show-drag-handle.js +88 -3
  14. package/dist/es2019/pm-plugins/decorations-anchor.js +6 -11
  15. package/dist/es2019/pm-plugins/decorations-common.js +4 -0
  16. package/dist/es2019/pm-plugins/main.js +24 -6
  17. package/dist/es2019/pm-plugins/utils/{fire-analytics.js → analytics.js} +31 -3
  18. package/dist/es2019/pm-plugins/utils/selection.js +22 -1
  19. package/dist/es2019/ui/drag-handle.js +19 -3
  20. package/dist/esm/editor-commands/move-node.js +17 -16
  21. package/dist/esm/editor-commands/move-to-layout.js +20 -8
  22. package/dist/esm/editor-commands/show-drag-handle.js +88 -2
  23. package/dist/esm/pm-plugins/decorations-anchor.js +6 -11
  24. package/dist/esm/pm-plugins/decorations-common.js +4 -0
  25. package/dist/esm/pm-plugins/main.js +27 -7
  26. package/dist/esm/pm-plugins/utils/{fire-analytics.js → analytics.js} +32 -3
  27. package/dist/esm/pm-plugins/utils/selection.js +21 -1
  28. package/dist/esm/ui/drag-handle.js +22 -4
  29. package/dist/types/editor-commands/show-drag-handle.d.ts +1 -1
  30. package/dist/types/pm-plugins/decorations-common.d.ts +1 -0
  31. package/dist/types/pm-plugins/main.d.ts +1 -0
  32. package/dist/types/pm-plugins/utils/analytics.d.ts +12 -0
  33. package/dist/types/pm-plugins/utils/selection.d.ts +9 -0
  34. package/dist/types-ts4.5/editor-commands/show-drag-handle.d.ts +1 -1
  35. package/dist/types-ts4.5/pm-plugins/decorations-common.d.ts +1 -0
  36. package/dist/types-ts4.5/pm-plugins/main.d.ts +1 -0
  37. package/dist/types-ts4.5/pm-plugins/utils/analytics.d.ts +12 -0
  38. package/dist/types-ts4.5/pm-plugins/utils/selection.d.ts +9 -0
  39. package/package.json +10 -4
  40. package/dist/cjs/pm-plugins/utils/fire-analytics.js +0 -36
  41. package/dist/types/pm-plugins/utils/fire-analytics.d.ts +0 -5
  42. package/dist/types-ts4.5/pm-plugins/utils/fire-analytics.d.ts +0 -5
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.DragHandle = void 0;
8
8
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
10
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
11
  var _react = require("react");
11
12
  var _react2 = require("@emotion/react");
@@ -25,18 +26,17 @@ var _primitives = require("@atlaskit/primitives");
25
26
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
26
27
  var _tooltip = _interopRequireDefault(require("@atlaskit/tooltip"));
27
28
  var _main = require("../pm-plugins/main");
29
+ var _analytics2 = require("../pm-plugins/utils/analytics");
28
30
  var _dragHandlePositions = require("../pm-plugins/utils/drag-handle-positions");
29
31
  var _getNestedNodePosition = require("../pm-plugins/utils/getNestedNodePosition");
30
32
  var _getSelection = require("../pm-plugins/utils/getSelection");
31
33
  var _consts = require("./consts");
32
34
  var _dragPreview = require("./drag-preview");
33
- /**
35
+ 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; }
36
+ 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) { (0, _defineProperty2.default)(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; } /**
34
37
  * @jsxRuntime classic
35
38
  * @jsx jsx
36
- */
37
-
38
- // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
39
-
39
+ */ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
40
40
  var iconWrapperStyles = (0, _primitives.xcss)({
41
41
  display: 'flex',
42
42
  justifyContent: 'center',
@@ -263,7 +263,11 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref) {
263
263
  var isMultiSelect = (0, _experiments.editorExperiment)('platform_editor_element_drag_and_drop_multiselect', true, {
264
264
  exposure: true
265
265
  });
266
+ var nodeTypes, hasSelectedMultipleNodes;
267
+ var resolvedMovingNode = tr.doc.resolve(start);
268
+ var maybeNode = resolvedMovingNode.nodeAfter;
266
269
  if (isMultiSelect) {
270
+ var _tr$getMeta;
267
271
  var handlePos = getPos();
268
272
  if (typeof handlePos !== 'number') {
269
273
  return tr;
@@ -274,22 +278,32 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref) {
274
278
  tr: tr
275
279
  });
276
280
  }
281
+ var multiSelectDnD = (_tr$getMeta = tr.getMeta(_main.key)) === null || _tr$getMeta === void 0 ? void 0 : _tr$getMeta.multiSelectDnD;
282
+ if (multiSelectDnD) {
283
+ var attributes = (0, _analytics2.getMultiSelectAnalyticsAttributes)(tr, multiSelectDnD.anchor, multiSelectDnD.head);
284
+ nodeTypes = attributes.nodeTypes;
285
+ hasSelectedMultipleNodes = attributes.hasSelectedMultipleNodes;
286
+ } else {
287
+ nodeTypes = maybeNode === null || maybeNode === void 0 ? void 0 : maybeNode.type.name;
288
+ hasSelectedMultipleNodes = false;
289
+ }
277
290
  }
278
291
  api === null || api === void 0 || (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 || _api$blockControls2.commands.setNodeDragged(getPos, anchorName, nodeType)({
279
292
  tr: tr
280
293
  });
281
- var resolvedMovingNode = tr.doc.resolve(start);
282
- var maybeNode = resolvedMovingNode.nodeAfter;
283
294
  tr.setMeta('scrollIntoView', false);
284
295
  api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 || _api$analytics3.actions.attachAnalyticsEvent({
285
296
  eventType: _analytics.EVENT_TYPE.UI,
286
297
  action: _analytics.ACTION.DRAGGED,
287
298
  actionSubject: _analytics.ACTION_SUBJECT.ELEMENT,
288
299
  actionSubjectId: _analytics.ACTION_SUBJECT_ID.ELEMENT_DRAG_HANDLE,
289
- attributes: {
300
+ attributes: _objectSpread({
290
301
  nodeDepth: resolvedMovingNode.depth,
291
302
  nodeType: (maybeNode === null || maybeNode === void 0 ? void 0 : maybeNode.type.name) || ''
292
- }
303
+ }, isMultiSelect && {
304
+ nodeTypes: nodeTypes,
305
+ hasSelectedMultipleNodes: hasSelectedMultipleNodes
306
+ })
293
307
  })(tr);
294
308
  return tr;
295
309
  });
@@ -11,12 +11,12 @@ import { findTable, isInTable, isTableSelected } from '@atlaskit/editor-tables/u
11
11
  import { fg } from '@atlaskit/platform-feature-flags';
12
12
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
13
13
  import { key } from '../pm-plugins/main';
14
+ import { attachMoveNodeAnalytics, getMultiSelectAnalyticsAttributes } from '../pm-plugins/utils/analytics';
14
15
  import { DIRECTION } from '../pm-plugins/utils/consts';
15
- import { attachMoveNodeAnalytics } from '../pm-plugins/utils/fire-analytics';
16
16
  import { getNestedNodePosition } from '../pm-plugins/utils/getNestedNodePosition';
17
17
  import { selectNode, setCursorPositionAtMovedNode } from '../pm-plugins/utils/getSelection';
18
18
  import { removeFromSource } from '../pm-plugins/utils/remove-from-source';
19
- import { getMultiSelectionIfPosInside } from '../pm-plugins/utils/selection';
19
+ import { getSelectedSlicePosition } from '../pm-plugins/utils/selection';
20
20
  import { getInsertLayoutStep, updateSelection } from '../pm-plugins/utils/update-selection';
21
21
  import { canMoveNodeToIndex, isInsideTable, transformSliceExpandToNestedExpand } from '../pm-plugins/utils/validation';
22
22
 
@@ -263,23 +263,21 @@ export const moveNode = api => (start, to, inputMethod = INPUT_METHOD.DRAG_AND_D
263
263
  }
264
264
  let sliceFrom = start;
265
265
  let sliceTo;
266
+ let mappedTo;
267
+ let sourceNodeTypes, hasSelectedMultipleNodes;
266
268
  const isMultiSelect = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true, {
267
269
  exposure: true
268
270
  });
269
271
  if (isMultiSelect) {
270
- var _handleNode$nodeSize;
271
- const {
272
- anchor,
273
- head
274
- } = getMultiSelectionIfPosInside(api, start);
275
- const inSelection = anchor !== undefined && head !== undefined;
276
- sliceFrom = inSelection ? Math.min(anchor, head) : start;
277
- const handleSize = (_handleNode$nodeSize = handleNode === null || handleNode === void 0 ? void 0 : handleNode.nodeSize) !== null && _handleNode$nodeSize !== void 0 ? _handleNode$nodeSize : 1;
278
- const handleEnd = sliceFrom + handleSize;
279
- sliceTo = inSelection ? Math.max(anchor, head) : handleEnd;
272
+ const slicePosition = getSelectedSlicePosition(start, tr, api);
273
+ sliceFrom = slicePosition.from;
274
+ sliceTo = slicePosition.to;
275
+ const attributes = getMultiSelectAnalyticsAttributes(tr, sliceFrom, sliceTo);
276
+ hasSelectedMultipleNodes = attributes.hasSelectedMultipleNodes;
277
+ sourceNodeTypes = attributes.nodeTypes;
280
278
  } else {
281
- var _handleNode$nodeSize2;
282
- const size = (_handleNode$nodeSize2 = handleNode === null || handleNode === void 0 ? void 0 : handleNode.nodeSize) !== null && _handleNode$nodeSize2 !== void 0 ? _handleNode$nodeSize2 : 1;
279
+ var _handleNode$nodeSize;
280
+ const size = (_handleNode$nodeSize = handleNode === null || handleNode === void 0 ? void 0 : handleNode.nodeSize) !== null && _handleNode$nodeSize !== void 0 ? _handleNode$nodeSize : 1;
283
281
  sliceTo = sliceFrom + size;
284
282
  }
285
283
  const {
@@ -288,7 +286,6 @@ export const moveNode = api => (start, to, inputMethod = INPUT_METHOD.DRAG_AND_D
288
286
  } = tr.doc.type.schema.nodes;
289
287
  const $to = tr.doc.resolve(to);
290
288
  const $handlePos = tr.doc.resolve(start);
291
- let mappedTo;
292
289
  if (editorExperiment('nested-dnd', true)) {
293
290
  const nodeCopy = tr.doc.slice(sliceFrom, sliceTo, false); // cut the content
294
291
  const destType = $to.node().type;
@@ -344,7 +341,7 @@ export const moveNode = api => (start, to, inputMethod = INPUT_METHOD.DRAG_AND_D
344
341
  }
345
342
  }
346
343
  if (editorExperiment('advanced_layouts', true)) {
347
- attachMoveNodeAnalytics(tr, inputMethod, $handlePos.depth, handleNode.type.name, $mappedTo === null || $mappedTo === void 0 ? void 0 : $mappedTo.depth, $mappedTo === null || $mappedTo === void 0 ? void 0 : $mappedTo.parent.type.name, $handlePos.sameParent($mappedTo), api);
344
+ attachMoveNodeAnalytics(tr, inputMethod, $handlePos.depth, handleNode.type.name, $mappedTo === null || $mappedTo === void 0 ? void 0 : $mappedTo.depth, $mappedTo === null || $mappedTo === void 0 ? void 0 : $mappedTo.parent.type.name, $handlePos.sameParent($mappedTo), api, sourceNodeTypes, hasSelectedMultipleNodes);
348
345
  } else {
349
346
  var _api$analytics;
350
347
  api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions.attachAnalyticsEvent({
@@ -359,6 +356,10 @@ export const moveNode = api => (start, to, inputMethod = INPUT_METHOD.DRAG_AND_D
359
356
  destinationNodeType: $mappedTo === null || $mappedTo === void 0 ? void 0 : $mappedTo.parent.type.name,
360
357
  ...(fg('platform_editor_element_drag_and_drop_ed_23873') && {
361
358
  inputMethod
359
+ }),
360
+ ...(isMultiSelect && {
361
+ sourceNodeTypes,
362
+ hasSelectedMultipleNodes
362
363
  })
363
364
  }
364
365
  })(tr);
@@ -4,9 +4,9 @@ import { Fragment, Node as PMNode } from '@atlaskit/editor-prosemirror/model';
4
4
  import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
5
5
  import { fg } from '@atlaskit/platform-feature-flags';
6
6
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
7
+ import { fireInsertLayoutAnalytics, attachMoveNodeAnalytics, getMultiSelectAnalyticsAttributes } from '../pm-plugins/utils/analytics';
7
8
  import { isFragmentOfType, containsNodeOfType } from '../pm-plugins/utils/check-fragment';
8
9
  import { maxLayoutColumnSupported } from '../pm-plugins/utils/consts';
9
- import { fireInsertLayoutAnalytics, attachMoveNodeAnalytics } from '../pm-plugins/utils/fire-analytics';
10
10
  import { removeFromSource } from '../pm-plugins/utils/remove-from-source';
11
11
  import { getMultiSelectionIfPosInside } from '../pm-plugins/utils/selection';
12
12
  import { updateColumnWidths } from '../pm-plugins/utils/update-column-widths';
@@ -42,9 +42,14 @@ const createNewLayout = (schema, layoutContents) => {
42
42
  const moveToExistingLayout = (toLayout, toLayoutPos, sourceContent, from, to, tr, $originalFrom, $originalTo, api, selectMovedNode) => {
43
43
  const isSameLayout = isInSameLayout($originalFrom, $originalTo);
44
44
  let sourceContentEndPos = -1;
45
- if (editorExperiment('platform_editor_element_drag_and_drop_multiselect', true)) {
45
+ const isMultiSelect = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true);
46
+ let sourceNodeTypes, hasSelectedMultipleNodes;
47
+ if (isMultiSelect) {
46
48
  if (sourceContent instanceof Fragment) {
47
49
  sourceContentEndPos = from + sourceContent.size;
50
+ const attributes = getMultiSelectAnalyticsAttributes(tr, from, sourceContentEndPos);
51
+ hasSelectedMultipleNodes = attributes.hasSelectedMultipleNodes;
52
+ sourceNodeTypes = attributes.nodeTypes;
48
53
  }
49
54
  } else {
50
55
  if (sourceContent instanceof PMNode) {
@@ -63,7 +68,7 @@ const moveToExistingLayout = (toLayout, toLayoutPos, sourceContent, from, to, tr
63
68
  if (!fg('platform_editor_advanced_layouts_post_fix_patch_1') || selectMovedNode) {
64
69
  tr.setSelection(new NodeSelection(tr.doc.resolve(mappedTo))).scrollIntoView();
65
70
  }
66
- attachMoveNodeAnalytics(tr, INPUT_METHOD.DRAG_AND_DROP, $originalFrom.depth, ((_$originalFrom$nodeAf = $originalFrom.nodeAfter) === null || _$originalFrom$nodeAf === void 0 ? void 0 : _$originalFrom$nodeAf.type.name) || '', 1, 'layoutSection', true, api);
71
+ attachMoveNodeAnalytics(tr, INPUT_METHOD.DRAG_AND_DROP, $originalFrom.depth, ((_$originalFrom$nodeAf = $originalFrom.nodeAfter) === null || _$originalFrom$nodeAf === void 0 ? void 0 : _$originalFrom$nodeAf.type.name) || '', 1, 'layoutSection', true, api, sourceNodeTypes, hasSelectedMultipleNodes);
67
72
  } else if (toLayout.childCount < maxLayoutColumnSupported()) {
68
73
  var _$originalFrom$nodeAf2;
69
74
  if (fg('platform_editor_advanced_layouts_post_fix_patch_1')) {
@@ -74,7 +79,7 @@ const moveToExistingLayout = (toLayout, toLayoutPos, sourceContent, from, to, tr
74
79
  const mappedFrom = tr.mapping.map(from);
75
80
  removeFromSource(tr, tr.doc.resolve(mappedFrom), tr.mapping.map(sourceContentEndPos));
76
81
  }
77
- attachMoveNodeAnalytics(tr, INPUT_METHOD.DRAG_AND_DROP, $originalFrom.depth, ((_$originalFrom$nodeAf2 = $originalFrom.nodeAfter) === null || _$originalFrom$nodeAf2 === void 0 ? void 0 : _$originalFrom$nodeAf2.type.name) || '', 1, 'layoutSection', false, api);
82
+ attachMoveNodeAnalytics(tr, INPUT_METHOD.DRAG_AND_DROP, $originalFrom.depth, ((_$originalFrom$nodeAf2 = $originalFrom.nodeAfter) === null || _$originalFrom$nodeAf2 === void 0 ? void 0 : _$originalFrom$nodeAf2.type.name) || '', 1, 'layoutSection', false, api, sourceNodeTypes, hasSelectedMultipleNodes);
78
83
  }
79
84
  return tr;
80
85
  };
@@ -197,7 +202,7 @@ const canMoveToLayout = (api, from, to, tr, moveNodeAtCursorPos) => {
197
202
  anchor,
198
203
  head
199
204
  } = getMultiSelectionIfPosInside(api, from);
200
- if (anchor && head) {
205
+ if (anchor !== undefined && head !== undefined) {
201
206
  sourceFrom = Math.min(anchor, head);
202
207
  sourceTo = Math.max(anchor, head);
203
208
  sourceContent = tr.doc.slice(sourceFrom, sourceTo).content;
@@ -249,7 +254,7 @@ const getBreakoutMode = (content, breakout) => {
249
254
  if (editorExperiment('platform_editor_element_drag_and_drop_multiselect', true)) {
250
255
  if (content instanceof PMNode) {
251
256
  var _content$marks$find;
252
- return (_content$marks$find = content.marks.find(m => m.type === breakout)) === null || _content$marks$find === void 0 ? void 0 : _content$marks$find.attrs;
257
+ return (_content$marks$find = content.marks.find(m => m.type === breakout)) === null || _content$marks$find === void 0 ? void 0 : _content$marks$find.attrs.mode;
253
258
  } else if (content instanceof Fragment) {
254
259
  // Find the first breakout mode in the fragment
255
260
  let firstBreakoutMode;
@@ -308,6 +313,7 @@ export const moveToLayout = api => (from, to, options) => ({
308
313
  if (!fromContentWithoutBreakout) {
309
314
  return tr;
310
315
  }
316
+ const isMultiSelect = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true);
311
317
  if (toNode.type === layoutSection) {
312
318
  const toPos = options !== null && options !== void 0 && options.moveToEnd ? to + toNode.nodeSize - 1 : to + 1;
313
319
  return moveToExistingLayout(toNode, to, fromContentWithoutBreakout, $sourceFrom.pos, toPos, tr, $sourceFrom, $to, api, options === null || options === void 0 ? void 0 : options.selectMovedNode);
@@ -325,7 +331,7 @@ export const moveToLayout = api => (from, to, options) => ({
325
331
  // resolve again the source node after node updated (remove breakout marks)
326
332
  toNodeWithoutBreakout = tr.doc.resolve(to).nodeAfter || toNode;
327
333
  }
328
- if (editorExperiment('platform_editor_element_drag_and_drop_multiselect', true)) {
334
+ if (isMultiSelect) {
329
335
  if (isFragmentOfType(fromContentWithoutBreakout, 'layoutColumn') && fromContentWithoutBreakout.firstChild) {
330
336
  fromContentWithoutBreakout = fromContentWithoutBreakout.firstChild.content;
331
337
  }
@@ -337,6 +343,12 @@ export const moveToLayout = api => (from, to, options) => ({
337
343
  const layoutContents = options !== null && options !== void 0 && options.moveToEnd ? [toNodeWithoutBreakout, fromContentWithoutBreakout] : [fromContentWithoutBreakout, toNodeWithoutBreakout];
338
344
  const newLayout = createNewLayout(tr.doc.type.schema, layoutContents);
339
345
  if (newLayout) {
346
+ let sourceNodeTypes, hasSelectedMultipleNodes;
347
+ if (isMultiSelect) {
348
+ const attributes = getMultiSelectAnalyticsAttributes(tr, $sourceFrom.pos, sourceTo);
349
+ hasSelectedMultipleNodes = attributes.hasSelectedMultipleNodes;
350
+ sourceNodeTypes = attributes.nodeTypes;
351
+ }
340
352
  tr = removeFromSource(tr, $sourceFrom, sourceTo);
341
353
  const mappedTo = tr.mapping.map(to);
342
354
  tr.delete(mappedTo, mappedTo + toNodeWithoutBreakout.nodeSize).insert(mappedTo, newLayout);
@@ -346,7 +358,7 @@ export const moveToLayout = api => (from, to, options) => ({
346
358
  breakoutMode && tr.setNodeMarkup(mappedTo, newLayout.type, newLayout.attrs, [breakout.create({
347
359
  mode: breakoutMode
348
360
  })]);
349
- fireInsertLayoutAnalytics(tr, api);
361
+ fireInsertLayoutAnalytics(tr, api, sourceNodeTypes, hasSelectedMultipleNodes);
350
362
  }
351
363
  return tr;
352
364
  }
@@ -1,8 +1,11 @@
1
+ import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
1
2
  import { isInTable } from '@atlaskit/editor-tables/utils';
3
+ import { fg } from '@atlaskit/platform-feature-flags';
2
4
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
3
- import { key } from '../pm-plugins/main';
5
+ import { findNodeDecs } from '../pm-plugins/decorations-anchor';
6
+ import { getDecorations, key } from '../pm-plugins/main';
4
7
  import { getNestedNodePosition } from '../pm-plugins/utils/getNestedNodePosition';
5
- export const showDragHandleAtSelection = (api, shouldFocusParentNode) => (state, _, view) => {
8
+ const showDragHandleAtSelectionOld = (api, shouldFocusParentNode) => (state, _, view) => {
6
9
  const {
7
10
  $from
8
11
  } = state.selection;
@@ -63,4 +66,86 @@ export const showDragHandleAtSelection = (api, shouldFocusParentNode) => (state,
63
66
  }
64
67
  }
65
68
  return false;
66
- };
69
+ };
70
+ const findParentPosForHandle = state => {
71
+ var _activeNode$handleOpt2;
72
+ const {
73
+ selection: {
74
+ $from
75
+ }
76
+ } = state;
77
+ const {
78
+ activeNode
79
+ } = key.getState(state) || {};
80
+
81
+ // if a node handle is already focused, return the parent pos of that node (with focused handle)
82
+ if (activeNode && (_activeNode$handleOpt2 = activeNode.handleOptions) !== null && _activeNode$handleOpt2 !== void 0 && _activeNode$handleOpt2.isFocused) {
83
+ const $activeNodePos = state.doc.resolve(activeNode.pos);
84
+
85
+ // if the handle is at the top level already, do nothing
86
+ if ($activeNodePos.depth === 0) {
87
+ return undefined;
88
+ }
89
+ return $activeNodePos.before();
90
+ }
91
+
92
+ // if we are in second level of nested node, we should focus the node at level 1
93
+ if ($from.depth <= 1) {
94
+ return $from.before(1);
95
+ }
96
+
97
+ // if we are inside a table, we should focus the table's handle
98
+ const parentTableNode = findParentNodeOfType([state.schema.nodes.table])(state.selection);
99
+ if (parentTableNode) {
100
+ return parentTableNode.pos;
101
+ }
102
+
103
+ // else find closest parent node
104
+ return getNestedNodePosition(state);
105
+ };
106
+ const findNextAnchorDecoration = state => {
107
+ const decorations = getDecorations(state);
108
+ if (!decorations) {
109
+ return undefined;
110
+ }
111
+ const nextHandleNodePos = findParentPosForHandle(state);
112
+ if (nextHandleNodePos === undefined) {
113
+ return undefined;
114
+ }
115
+ const nextHandleNode = state.doc.nodeAt(nextHandleNodePos);
116
+ let nodeDecorations = nextHandleNode && findNodeDecs(decorations, nextHandleNodePos, nextHandleNodePos + nextHandleNode.nodeSize);
117
+ if (!nodeDecorations || nodeDecorations.length === 0) {
118
+ return undefined;
119
+ }
120
+
121
+ // ensure the decoration covers the position of the look up node
122
+ nodeDecorations = nodeDecorations.filter(decoration => {
123
+ return decoration.from <= nextHandleNodePos;
124
+ });
125
+ if (nodeDecorations.length === 0) {
126
+ return undefined;
127
+ }
128
+
129
+ // sort the decorations by the position of the node
130
+ // so we can find the closest decoration to the node
131
+ nodeDecorations.sort((a, b) => {
132
+ if (a.from === b.from) {
133
+ return a.to - b.to;
134
+ }
135
+ return b.from - a.from;
136
+ });
137
+
138
+ // return the closest decoration to the node
139
+ return nodeDecorations[0];
140
+ };
141
+ const showDragHandleAtSelectionNew = api => state => {
142
+ const decoration = findNextAnchorDecoration(state);
143
+ if (api && decoration) {
144
+ api.core.actions.execute(api.blockControls.commands.showDragHandleAt(decoration.from, decoration.spec.anchorName, decoration.spec.nodeTypeWithLevel, {
145
+ isFocused: true
146
+ }));
147
+ return true;
148
+ }
149
+ return false;
150
+ };
151
+ export const showDragHandleAtSelection = api => (state, dispatch, view) => editorExperiment('nested-dnd', true) && fg('platform_editor_advanced_layouts_a11y') ? showDragHandleAtSelectionNew(api)(state) : showDragHandleAtSelectionOld(api)(state, dispatch, view);
@@ -1,7 +1,7 @@
1
1
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
2
2
  import { fg } from '@atlaskit/platform-feature-flags';
3
3
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
4
- import { getNestedDepth, getNodeAnchor, TYPE_NODE_DEC } from './decorations-common';
4
+ import { getNestedDepth, getNodeAnchor, getNodeTypeWithLevel, TYPE_NODE_DEC } from './decorations-common';
5
5
  const IGNORE_NODES = ['tableCell', 'tableHeader', 'tableRow', 'listItem', 'caption', 'layoutColumn'];
6
6
  const IGNORE_NODES_NEXT = ['tableCell', 'tableHeader', 'tableRow', 'listItem', 'caption'];
7
7
  const IGNORE_NODE_DESCENDANTS = ['listItem', 'taskList', 'decisionList', 'mediaSingle'];
@@ -68,11 +68,10 @@ export const nodeDecorations = (newState, from, to) => {
68
68
  const ignore_nodes = editorExperiment('advanced_layouts', true) ? IGNORE_NODES_NEXT : IGNORE_NODES;
69
69
  newState.doc.nodesBetween(docFrom, docTo, (node, pos, parent, index) => {
70
70
  let depth = 0;
71
- let anchorName;
72
71
  const shouldDescend = shouldDescendIntoNode(node);
73
- anchorName = getNodeAnchor(node);
72
+ const anchorName = getNodeAnchor(node);
73
+ const nodeTypeWithLevel = getNodeTypeWithLevel(node);
74
74
  if (editorExperiment('nested-dnd', true)) {
75
- var _anchorName;
76
75
  // Doesn't descend into a node
77
76
  if (node.isInline) {
78
77
  return false;
@@ -81,22 +80,18 @@ export const nodeDecorations = (newState, from, to) => {
81
80
  if (shouldIgnoreNode(node, ignore_nodes, depth, parent)) {
82
81
  return shouldDescend; //skip over, don't consider it a valid depth
83
82
  }
84
- anchorName = (_anchorName = anchorName) !== null && _anchorName !== void 0 ? _anchorName : `--node-anchor-${node.type.name}-${pos}`;
85
- } else {
86
- var _anchorName2;
87
- anchorName = (_anchorName2 = anchorName) !== null && _anchorName2 !== void 0 ? _anchorName2 : `--node-anchor-${node.type.name}-${index}`;
88
83
  }
89
84
  const anchorStyles = `anchor-name: ${anchorName};`;
90
- const subType = node.attrs.level ? `-${node.attrs.level}` : '';
91
85
  decs.push(Decoration.node(pos, pos + node.nodeSize, {
92
86
  style: anchorStyles,
93
87
  ['data-drag-handler-anchor-name']: anchorName,
94
- ['data-drag-handler-node-type']: node.type.name + subType,
88
+ ['data-drag-handler-node-type']: nodeTypeWithLevel,
95
89
  ['data-drag-handler-anchor-depth']: `${depth}`
96
90
  }, {
97
91
  type: TYPE_NODE_DEC,
98
92
  anchorName,
99
- nodeType: node.type.name
93
+ nodeType: node.type.name,
94
+ nodeTypeWithLevel
100
95
  }));
101
96
  return shouldDescend && depth < getNestedDepth();
102
97
  });
@@ -9,6 +9,10 @@ export const getNodeAnchor = node => {
9
9
  const handleId = ObjHash.getForNode(node);
10
10
  return `--node-anchor-${node.type.name}-${handleId}`;
11
11
  };
12
+ export const getNodeTypeWithLevel = node => {
13
+ const subType = node.attrs.level ? `-${node.attrs.level}` : '';
14
+ return node.type.name + subType;
15
+ };
12
16
  class ObjHash {
13
17
  static getForNode(node) {
14
18
  if (this.caching.has(node)) {
@@ -18,7 +18,9 @@ import { dropTargetDecorations, findDropTargetDecs } from './decorations-drop-ta
18
18
  import { handleMouseOver } from './handle-mouse-over';
19
19
  import { boundKeydownHandler } from './keymap';
20
20
  import { defaultActiveAnchorTracker } from './utils/active-anchor-tracker';
21
+ import { getMultiSelectAnalyticsAttributes } from './utils/analytics';
21
22
  import { AnchorRectCache, isAnchorSupported } from './utils/anchor-utils';
23
+ import { getSelectedSlicePosition } from './utils/selection';
22
24
  import { getTrMetadata } from './utils/transactions';
23
25
  export const key = new PluginKey('blockControls');
24
26
  const EDITOR_BLOCKS_DRAG_INIT = 'Editor Blocks Drag Initialization Time';
@@ -73,7 +75,8 @@ const destroyFn = (api, editorView) => {
73
75
  api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(({
74
76
  tr
75
77
  }) => {
76
- if (editorExperiment('platform_editor_element_drag_and_drop_multiselect', true)) {
78
+ const isMultiSelect = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true);
79
+ if (isMultiSelect) {
77
80
  var _api$blockControls, _api$selection;
78
81
  const {
79
82
  multiSelectDnD
@@ -96,6 +99,13 @@ const destroyFn = (api, editorView) => {
96
99
  // if no drop targets are rendered, assume that drop is invalid
97
100
  if (location.current.dropTargets.length === 0) {
98
101
  var _api$analytics2;
102
+ let nodeTypes, hasSelectedMultipleNodes;
103
+ if (isMultiSelect && api) {
104
+ const position = getSelectedSlicePosition(start, tr, api);
105
+ const attributes = getMultiSelectAnalyticsAttributes(tr, position.from, position.to);
106
+ nodeTypes = attributes.nodeTypes;
107
+ hasSelectedMultipleNodes = attributes.hasSelectedMultipleNodes;
108
+ }
99
109
  const resolvedMovingNode = tr.doc.resolve(start);
100
110
  const maybeNode = resolvedMovingNode.nodeAfter;
101
111
  api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions.attachAnalyticsEvent({
@@ -105,7 +115,11 @@ const destroyFn = (api, editorView) => {
105
115
  actionSubjectId: ACTION_SUBJECT_ID.ELEMENT_DRAG_HANDLE,
106
116
  attributes: {
107
117
  nodeDepth: resolvedMovingNode.depth,
108
- nodeType: (maybeNode === null || maybeNode === void 0 ? void 0 : maybeNode.type.name) || ''
118
+ nodeType: (maybeNode === null || maybeNode === void 0 ? void 0 : maybeNode.type.name) || '',
119
+ ...(isMultiSelect && {
120
+ nodeTypes,
121
+ hasSelectedMultipleNodes
122
+ })
109
123
  }
110
124
  })(tr);
111
125
  }
@@ -131,6 +145,10 @@ const initialState = {
131
145
  isPMDragging: false,
132
146
  multiSelectDnD: undefined
133
147
  };
148
+ export const getDecorations = state => {
149
+ var _key$getState;
150
+ return (_key$getState = key.getState(state)) === null || _key$getState === void 0 ? void 0 : _key$getState.decorations;
151
+ };
134
152
  export const newApply = (api, formatMessage, tr, currentState, newState, flags, nodeViewPortalProviderAPI, anchorRectCache) => {
135
153
  var _meta$activeNode, _activeNode, _activeNode2, _meta$activeNode$hand, _meta$isDragging, _meta$isDragging2, _meta$editorHeight, _meta$editorWidthLeft, _meta$editorWidthRigh, _meta$isPMDragging, _meta$multiSelectDnD;
136
154
  let {
@@ -452,12 +470,12 @@ export const createPlugin = (api, getIntl, nodeViewPortalProviderAPI) => {
452
470
  },
453
471
  props: {
454
472
  decorations: state => {
455
- var _api$editorDisabled, _api$editorDisabled$s, _key$getState;
473
+ var _api$editorDisabled, _api$editorDisabled$s, _key$getState2;
456
474
  const isDisabled = api === null || api === void 0 ? void 0 : (_api$editorDisabled = api.editorDisabled) === null || _api$editorDisabled === void 0 ? void 0 : (_api$editorDisabled$s = _api$editorDisabled.sharedState.currentState()) === null || _api$editorDisabled$s === void 0 ? void 0 : _api$editorDisabled$s.editorDisabled;
457
475
  if (isDisabled) {
458
476
  return;
459
477
  }
460
- return (_key$getState = key.getState(state)) === null || _key$getState === void 0 ? void 0 : _key$getState.decorations;
478
+ return (_key$getState2 = key.getState(state)) === null || _key$getState2 === void 0 ? void 0 : _key$getState2.decorations;
461
479
  },
462
480
  handleDOMEvents: {
463
481
  drop(view, event) {
@@ -532,12 +550,12 @@ export const createPlugin = (api, getIntl, nodeViewPortalProviderAPI) => {
532
550
  }));
533
551
  },
534
552
  dragend(view) {
535
- var _key$getState2;
553
+ var _key$getState3;
536
554
  const {
537
555
  state,
538
556
  dispatch
539
557
  } = view;
540
- if ((_key$getState2 = key.getState(state)) !== null && _key$getState2 !== void 0 && _key$getState2.isPMDragging) {
558
+ if ((_key$getState3 = key.getState(state)) !== null && _key$getState3 !== void 0 && _key$getState3.isPMDragging) {
541
559
  dispatch(state.tr.setMeta(key, {
542
560
  isPMDragging: false
543
561
  }));
@@ -1,5 +1,6 @@
1
1
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
2
- export const attachMoveNodeAnalytics = (tr, inputMethod, fromDepth, fromNodeType, toDepth, toNodeType, isSameParent, api) => {
2
+ import { fg } from '@atlaskit/platform-feature-flags';
3
+ export const attachMoveNodeAnalytics = (tr, inputMethod, fromDepth, fromNodeType, toDepth, toNodeType, isSameParent, api, fromNodeTypes, hasSelectedMultipleNodes) => {
3
4
  var _api$analytics, _api$analytics$action;
4
5
  return api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : (_api$analytics$action = _api$analytics.actions) === null || _api$analytics$action === void 0 ? void 0 : _api$analytics$action.attachAnalyticsEvent({
5
6
  eventType: EVENT_TYPE.TRACK,
@@ -9,6 +10,8 @@ export const attachMoveNodeAnalytics = (tr, inputMethod, fromDepth, fromNodeType
9
10
  attributes: {
10
11
  nodeDepth: fromDepth,
11
12
  nodeType: fromNodeType,
13
+ nodeTypes: fromNodeTypes,
14
+ hasSelectedMultipleNodes,
12
15
  destinationNodeDepth: toDepth,
13
16
  destinationNodeType: toNodeType,
14
17
  isSameParent: isSameParent,
@@ -16,15 +19,40 @@ export const attachMoveNodeAnalytics = (tr, inputMethod, fromDepth, fromNodeType
16
19
  }
17
20
  })(tr);
18
21
  };
19
- export const fireInsertLayoutAnalytics = (tr, api) => {
22
+ export const fireInsertLayoutAnalytics = (tr, api, nodeTypes, hasSelectedMultipleNodes) => {
20
23
  var _api$analytics2, _api$analytics2$actio;
21
24
  api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : (_api$analytics2$actio = _api$analytics2.actions) === null || _api$analytics2$actio === void 0 ? void 0 : _api$analytics2$actio.attachAnalyticsEvent({
22
25
  action: ACTION.INSERTED,
23
26
  actionSubject: ACTION_SUBJECT.DOCUMENT,
24
27
  actionSubjectId: ACTION_SUBJECT_ID.LAYOUT,
25
28
  attributes: {
26
- inputMethod: INPUT_METHOD.DRAG_AND_DROP
29
+ inputMethod: INPUT_METHOD.DRAG_AND_DROP,
30
+ nodeTypes,
31
+ hasSelectedMultipleNodes
27
32
  },
28
33
  eventType: EVENT_TYPE.TRACK
29
34
  })(tr);
35
+ };
36
+
37
+ /**
38
+ * Given a range, return distinctive types of node and whether there are multiple nodes in the range
39
+ */
40
+ export const getMultiSelectAnalyticsAttributes = (tr, anchor, head) => {
41
+ const nodeTypes = [];
42
+ const from = Math.min(anchor, head);
43
+ const to = Math.max(anchor, head);
44
+ tr.doc.nodesBetween(from, to, (node, pos) => {
45
+ if (pos < from) {
46
+ // ignore parent node
47
+ return true;
48
+ }
49
+ nodeTypes.push(node.type.name);
50
+
51
+ // only care about the top level (relatively in the range) nodes
52
+ return false;
53
+ });
54
+ return {
55
+ nodeTypes: fg('platform_editor_track_node_types') ? [...new Set(nodeTypes)].sort().join(',') : undefined,
56
+ hasSelectedMultipleNodes: nodeTypes.length > 1
57
+ };
30
58
  };
@@ -2,7 +2,7 @@ export const getMultiSelectionIfPosInside = (api, pos) => {
2
2
  var _api$blockControls;
3
3
  const {
4
4
  multiSelectDnD
5
- } = (api === null || api === void 0 ? void 0 : (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.sharedState.currentState()) || {};
5
+ } = ((_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.sharedState.currentState()) || {};
6
6
  if (multiSelectDnD && multiSelectDnD.anchor >= 0 && multiSelectDnD.head >= 0) {
7
7
  const multiFrom = Math.min(multiSelectDnD.anchor, multiSelectDnD.head);
8
8
  const multiTo = Math.max(multiSelectDnD.anchor, multiSelectDnD.head);
@@ -14,4 +14,25 @@ export const getMultiSelectionIfPosInside = (api, pos) => {
14
14
  } : {};
15
15
  }
16
16
  return {};
17
+ };
18
+
19
+ /**
20
+ *
21
+ * @returns from and to positions of the selected content (after expansion)
22
+ */
23
+ export const getSelectedSlicePosition = (handlePos, tr, api) => {
24
+ var _activeNode$nodeSize;
25
+ const {
26
+ anchor,
27
+ head
28
+ } = getMultiSelectionIfPosInside(api, handlePos);
29
+ const inSelection = anchor !== undefined && head !== undefined;
30
+ const from = inSelection ? Math.min(anchor, head) : handlePos;
31
+ const activeNode = tr.doc.nodeAt(handlePos);
32
+ const activeNodeEndPos = handlePos + ((_activeNode$nodeSize = activeNode === null || activeNode === void 0 ? void 0 : activeNode.nodeSize) !== null && _activeNode$nodeSize !== void 0 ? _activeNode$nodeSize : 1);
33
+ const to = inSelection ? Math.max(anchor, head) : activeNodeEndPos;
34
+ return {
35
+ from,
36
+ to
37
+ };
17
38
  };
@@ -21,6 +21,7 @@ import { Box, xcss } from '@atlaskit/primitives';
21
21
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
22
22
  import Tooltip from '@atlaskit/tooltip';
23
23
  import { key } from '../pm-plugins/main';
24
+ import { getMultiSelectAnalyticsAttributes } from '../pm-plugins/utils/analytics';
24
25
  import { getLeftPosition, getTopPosition } from '../pm-plugins/utils/drag-handle-positions';
25
26
  import { getNestedNodePosition } from '../pm-plugins/utils/getNestedNodePosition';
26
27
  import { isHandleInSelection, selectNode } from '../pm-plugins/utils/getSelection';
@@ -246,7 +247,11 @@ export const DragHandle = ({
246
247
  const isMultiSelect = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true, {
247
248
  exposure: true
248
249
  });
250
+ let nodeTypes, hasSelectedMultipleNodes;
251
+ const resolvedMovingNode = tr.doc.resolve(start);
252
+ const maybeNode = resolvedMovingNode.nodeAfter;
249
253
  if (isMultiSelect) {
254
+ var _tr$getMeta;
250
255
  const handlePos = getPos();
251
256
  if (typeof handlePos !== 'number') {
252
257
  return tr;
@@ -257,12 +262,19 @@ export const DragHandle = ({
257
262
  tr
258
263
  });
259
264
  }
265
+ const multiSelectDnD = (_tr$getMeta = tr.getMeta(key)) === null || _tr$getMeta === void 0 ? void 0 : _tr$getMeta.multiSelectDnD;
266
+ if (multiSelectDnD) {
267
+ const attributes = getMultiSelectAnalyticsAttributes(tr, multiSelectDnD.anchor, multiSelectDnD.head);
268
+ nodeTypes = attributes.nodeTypes;
269
+ hasSelectedMultipleNodes = attributes.hasSelectedMultipleNodes;
270
+ } else {
271
+ nodeTypes = maybeNode === null || maybeNode === void 0 ? void 0 : maybeNode.type.name;
272
+ hasSelectedMultipleNodes = false;
273
+ }
260
274
  }
261
275
  api === null || api === void 0 ? void 0 : (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 ? void 0 : _api$blockControls2.commands.setNodeDragged(getPos, anchorName, nodeType)({
262
276
  tr
263
277
  });
264
- const resolvedMovingNode = tr.doc.resolve(start);
265
- const maybeNode = resolvedMovingNode.nodeAfter;
266
278
  tr.setMeta('scrollIntoView', false);
267
279
  api === null || api === void 0 ? void 0 : (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions.attachAnalyticsEvent({
268
280
  eventType: EVENT_TYPE.UI,
@@ -271,7 +283,11 @@ export const DragHandle = ({
271
283
  actionSubjectId: ACTION_SUBJECT_ID.ELEMENT_DRAG_HANDLE,
272
284
  attributes: {
273
285
  nodeDepth: resolvedMovingNode.depth,
274
- nodeType: (maybeNode === null || maybeNode === void 0 ? void 0 : maybeNode.type.name) || ''
286
+ nodeType: (maybeNode === null || maybeNode === void 0 ? void 0 : maybeNode.type.name) || '',
287
+ ...(isMultiSelect && {
288
+ nodeTypes,
289
+ hasSelectedMultipleNodes
290
+ })
275
291
  }
276
292
  })(tr);
277
293
  return tr;