@atlaskit/editor-plugin-paste 2.3.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @atlaskit/editor-plugin-paste
2
2
 
3
+ ## 3.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#117363](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/117363)
8
+ [`10a0f7f6c2027`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/10a0f7f6c2027) -
9
+ This package's `peerDependencies` have been adjusted for `react` and/or `react-dom` to reflect the
10
+ status of only supporting React 18 going forward. No explicit breaking change to React support has
11
+ been made in this release, but this is to signify going forward, breaking changes for React 16 or
12
+ React 17 may come via non-major semver releases.
13
+
14
+ Please refer this community post for more details:
15
+ https://community.developer.atlassian.com/t/rfc-78-dropping-support-for-react-16-and-rendering-in-a-react-18-concurrent-root-in-jira-and-confluence/87026
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies
20
+
21
+ ## 2.3.1
22
+
23
+ ### Patch Changes
24
+
25
+ - [#114384](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/114384)
26
+ [`72325f32502fb`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/72325f32502fb) -
27
+ [ED-26291] Update document moved event to reflect multiple selection information
28
+ - Updated dependencies
29
+
3
30
  ## 2.3.0
4
31
 
5
32
  ### Minor Changes
@@ -17,7 +17,9 @@ var updateContentMoved = exports.updateContentMoved = function updateContentMove
17
17
  currentActions: [].concat((0, _toConsumableArray2.default)(contentMoved.currentActions), [nextAction]),
18
18
  size: (nextState === null || nextState === void 0 ? void 0 : nextState.size) || contentMoved.size,
19
19
  nodeName: nextState === null || nextState === void 0 ? void 0 : nextState.nodeName,
20
- nodeDepth: nextState === null || nextState === void 0 ? void 0 : nextState.nodeDepth
20
+ nodeDepth: nextState === null || nextState === void 0 ? void 0 : nextState.nodeDepth,
21
+ nodeTypes: nextState === null || nextState === void 0 ? void 0 : nextState.nodeTypes,
22
+ hasSelectedMultipleNodes: nextState === null || nextState === void 0 ? void 0 : nextState.hasSelectedMultipleNodes
21
23
  };
22
24
  return {
23
25
  type: _actions.MoveAnalyticPluginTypes.UpdateMovedAction,
@@ -1,16 +1,21 @@
1
1
  "use strict";
2
2
 
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
3
4
  Object.defineProperty(exports, "__esModule", {
4
5
  value: true
5
6
  });
6
7
  exports.createPlugin = void 0;
8
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
7
9
  var _analytics = require("@atlaskit/editor-common/analytics");
8
10
  var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
11
+ var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
9
12
  var _commands = require("./commands");
10
13
  var _pluginFactory = require("./plugin-factory");
11
14
  var _pluginKey = require("./plugin-key");
12
15
  var _types = require("./types");
13
16
  var _utils = require("./utils");
17
+ 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; }
18
+ 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; }
14
19
  // This plugin exists only in FullPage/FullWidth Editor and is used to register an event that tells us
15
20
  // that a user cut and than pasted a node. This order of actions could be considered an alternative
16
21
  // to new Drag and Drop functionality. The event (document moved) is not accurate, but should be enough to be
@@ -51,11 +56,12 @@ var createPlugin = exports.createPlugin = function createPlugin(dispatch, editor
51
56
  actionSubjectId: _analytics.ACTION_SUBJECT_ID.NODE,
52
57
  eventType: _analytics.EVENT_TYPE.TRACK,
53
58
  attributes: {
59
+ // keep nodeName from copied slice
54
60
  nodeType: contentMoved === null || contentMoved === void 0 ? void 0 : contentMoved.nodeName,
55
61
  nodeDepth: contentMoved === null || contentMoved === void 0 ? void 0 : contentMoved.nodeDepth,
56
- destinationNodeDepth: (0, _utils.getParentNodeDepth)(state.selection)
57
-
58
- // keep nodeName from copied slice
62
+ destinationNodeDepth: (0, _utils.getParentNodeDepth)(state.selection),
63
+ nodeTypes: contentMoved === null || contentMoved === void 0 ? void 0 : contentMoved.nodeTypes,
64
+ hasSelectedMultipleNodes: contentMoved === null || contentMoved === void 0 ? void 0 : contentMoved.hasSelectedMultipleNodes
59
65
  }
60
66
  })(tr);
61
67
 
@@ -74,6 +80,9 @@ var createPlugin = exports.createPlugin = function createPlugin(dispatch, editor
74
80
  var resetState = false;
75
81
  var content = slice.content,
76
82
  size = slice.size;
83
+ var selection = state.selection;
84
+ // Note: the following is not the case once `platform_editor_element_drag_and_drop_multiselect` is enabled
85
+ // we now want to track cut events for multiple nodes
77
86
  // Content should be just one node, so we added a check for slice.content.childCount === 1;
78
87
  // 1. It is possible to select a table by dragging the mouse over the table's rows.
79
88
  // As a result, slice will contain rows without tableNode itself and the childCount will be the number of rows.
@@ -82,35 +91,57 @@ var createPlugin = exports.createPlugin = function createPlugin(dispatch, editor
82
91
  // the paragraph below the node. Visually only the node in between is selected, in reality, three nodes are
83
92
  // in the slice.
84
93
  // These cases are ignored and moveContent event won't be counted.
85
- if (content.childCount !== 1) {
86
- resetState = true;
87
- }
94
+ var isMultiSelectEnabled = (0, _experiments.editorExperiment)('platform_editor_element_drag_and_drop_multiselect', true);
88
95
  var nodeName = ((_content$firstChild2 = content.firstChild) === null || _content$firstChild2 === void 0 ? void 0 : _content$firstChild2.type.name) || '';
89
-
90
- // Some nodes are not relevant as they are parts of nodes, not whole nodes (like tableCell, tableHeader instead of table node)
91
- // Some nodes like lists, taskList(item), decisionList(item) requires tricky checks that we want to avoid doing.
92
- // These nodes were added to excludedNodes array.
93
- if (!resetState && (0, _utils.isExcludedNode)(nodeName)) {
94
- resetState = true;
95
- }
96
- var selection = state.selection;
97
- if (!resetState && !(0, _utils.isEntireNestedParagraphOrHeadingSelected)(selection)) {
98
- resetState = true;
99
- }
100
- if (!resetState && (0, _utils.isInlineNode)(nodeName) && (0, _utils.isNestedInlineNode)(selection)) {
101
- resetState = true;
102
- }
103
- if (!resetState && (0, _utils.isNestedInTable)(state)) {
96
+ var nodeTypes,
97
+ hasSelectedMultipleNodes = false;
98
+ if (content.childCount > 1) {
99
+ if (isMultiSelectEnabled) {
100
+ if ((0, _utils.containsExcludedNode)(content)) {
101
+ resetState = true;
102
+ } else {
103
+ var attributes = (0, _utils.getMultipleSelectionAttributes)(content);
104
+ nodeTypes = attributes.nodeTypes;
105
+ hasSelectedMultipleNodes = attributes.hasSelectedMultipleNodes;
106
+ }
107
+ } else {
108
+ resetState = true;
109
+ }
110
+ } else if (content.childCount === 1) {
111
+ // Some nodes are not relevant as they are parts of nodes, not whole nodes (like tableCell, tableHeader instead of table node)
112
+ // Some nodes like lists, taskList(item), decisionList(item) requires tricky checks that we want to avoid doing.
113
+ // These nodes were added to excludedNodes array.
114
+ if (!resetState && (0, _utils.isExcludedNode)(nodeName)) {
115
+ resetState = true;
116
+ }
117
+ if (!resetState && !(0, _utils.isEntireNestedParagraphOrHeadingSelected)(selection)) {
118
+ resetState = true;
119
+ }
120
+ if (!resetState && (0, _utils.isInlineNode)(nodeName) && (0, _utils.isNestedInlineNode)(selection)) {
121
+ resetState = true;
122
+ }
123
+ if (!resetState && (0, _utils.isNestedInTable)(state)) {
124
+ resetState = true;
125
+ }
126
+ } else {
104
127
  resetState = true;
105
128
  }
106
129
  if (resetState) {
107
130
  (0, _commands.resetContentMoved)()(state, dispatch);
108
131
  } else {
109
- (0, _commands.updateContentMoved)({
132
+ var newState = {
110
133
  size: size,
111
134
  nodeName: nodeName,
112
135
  nodeDepth: (0, _utils.getParentNodeDepth)(selection)
113
- }, 'contentCut')(state, dispatch);
136
+ };
137
+ if (isMultiSelectEnabled) {
138
+ var _nodeTypes;
139
+ newState = _objectSpread(_objectSpread({}, newState), {}, {
140
+ nodeTypes: (_nodeTypes = nodeTypes) !== null && _nodeTypes !== void 0 ? _nodeTypes : nodeName,
141
+ hasSelectedMultipleNodes: hasSelectedMultipleNodes
142
+ });
143
+ }
144
+ (0, _commands.updateContentMoved)(newState, 'contentCut')(state, dispatch);
114
145
  }
115
146
  isCutEvent = false;
116
147
  return slice;
@@ -15,6 +15,8 @@ var defaultState = exports.defaultState = {
15
15
  nodeName: undefined,
16
16
  size: undefined,
17
17
  nodeDepth: undefined,
18
- currentActions: []
18
+ currentActions: [],
19
+ nodeTypes: undefined,
20
+ hasSelectedMultipleNodes: undefined
19
21
  }
20
22
  };
@@ -1,11 +1,14 @@
1
1
  "use strict";
2
2
 
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
3
4
  Object.defineProperty(exports, "__esModule", {
4
5
  value: true
5
6
  });
6
- exports.isNestedInlineNode = exports.isNestedInTable = exports.isInlineNode = exports.isExcludedNode = exports.isEntireNestedParagraphOrHeadingSelected = exports.isCursorSelectionAtTopLevel = exports.getParentNodeDepth = void 0;
7
+ exports.isNestedInlineNode = exports.isNestedInTable = exports.isInlineNode = exports.isExcludedNode = exports.isEntireNestedParagraphOrHeadingSelected = exports.isCursorSelectionAtTopLevel = exports.getParentNodeDepth = exports.getMultipleSelectionAttributes = exports.containsExcludedNode = void 0;
8
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
7
9
  var _utils = require("@atlaskit/editor-prosemirror/utils");
8
10
  var _cellSelection = require("@atlaskit/editor-tables/cell-selection");
11
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
9
12
  var excludedNodes = ['caption', 'layoutColumn', 'listItem', 'tableHeader', 'tableCell', 'tableRow', 'text', 'placeholder', 'unsupportedBlock', 'unsupportedInline', 'hardBreak', 'confluenceUnsupportedBlock', 'confluenceUnsupportedInline', 'taskItem', 'decisionItem'];
10
13
  var isExcludedNode = exports.isExcludedNode = function isExcludedNode(nodeName) {
11
14
  return excludedNodes.includes(nodeName);
@@ -69,4 +72,26 @@ var isEntireNestedParagraphOrHeadingSelected = exports.isEntireNestedParagraphOr
69
72
  var $from = selection.$from,
70
73
  $to = selection.$to;
71
74
  return $from.textOffset === 0 && $to.textOffset === 0;
75
+ };
76
+ var containsExcludedNode = exports.containsExcludedNode = function containsExcludedNode(content) {
77
+ for (var i = 0; i < content.childCount; i++) {
78
+ var _content$maybeChild;
79
+ var nodeName = ((_content$maybeChild = content.maybeChild(i)) === null || _content$maybeChild === void 0 ? void 0 : _content$maybeChild.type.name) || '';
80
+ if (isExcludedNode(nodeName)) {
81
+ return true;
82
+ }
83
+ }
84
+ return false;
85
+ };
86
+ var getMultipleSelectionAttributes = exports.getMultipleSelectionAttributes = function getMultipleSelectionAttributes(content) {
87
+ var nodeTypes = [];
88
+ if (content.size) {
89
+ content.forEach(function (node) {
90
+ nodeTypes.push(node.type.name);
91
+ });
92
+ }
93
+ return {
94
+ nodeTypes: (0, _platformFeatureFlags.fg)('platform_editor_track_node_types') ? (0, _toConsumableArray2.default)(new Set(nodeTypes)).sort().join(',') : undefined,
95
+ hasSelectedMultipleNodes: nodeTypes.length > 1
96
+ };
72
97
  };
@@ -9,7 +9,9 @@ export const updateContentMoved = (nextState, nextAction) => createCommand(state
9
9
  currentActions: [...contentMoved.currentActions, nextAction],
10
10
  size: (nextState === null || nextState === void 0 ? void 0 : nextState.size) || contentMoved.size,
11
11
  nodeName: nextState === null || nextState === void 0 ? void 0 : nextState.nodeName,
12
- nodeDepth: nextState === null || nextState === void 0 ? void 0 : nextState.nodeDepth
12
+ nodeDepth: nextState === null || nextState === void 0 ? void 0 : nextState.nodeDepth,
13
+ nodeTypes: nextState === null || nextState === void 0 ? void 0 : nextState.nodeTypes,
14
+ hasSelectedMultipleNodes: nextState === null || nextState === void 0 ? void 0 : nextState.hasSelectedMultipleNodes
13
15
  };
14
16
  return {
15
17
  type: MoveAnalyticPluginTypes.UpdateMovedAction,
@@ -1,10 +1,11 @@
1
1
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
2
2
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
3
4
  import { resetContentMoved, resetContentMovedTransform, updateContentMoved } from './commands';
4
5
  import { createPluginState, getPluginState } from './plugin-factory';
5
6
  import { pluginKey } from './plugin-key';
6
7
  import { defaultState } from './types';
7
- import { getParentNodeDepth, isCursorSelectionAtTopLevel, isEntireNestedParagraphOrHeadingSelected, isExcludedNode, isInlineNode, isNestedInlineNode, isNestedInTable } from './utils';
8
+ import { containsExcludedNode, getMultipleSelectionAttributes, getParentNodeDepth, isCursorSelectionAtTopLevel, isEntireNestedParagraphOrHeadingSelected, isExcludedNode, isInlineNode, isNestedInlineNode, isNestedInTable } from './utils';
8
9
 
9
10
  // This plugin exists only in FullPage/FullWidth Editor and is used to register an event that tells us
10
11
  // that a user cut and than pasted a node. This order of actions could be considered an alternative
@@ -52,11 +53,12 @@ export const createPlugin = (dispatch, editorAnalyticsAPI) => {
52
53
  actionSubjectId: ACTION_SUBJECT_ID.NODE,
53
54
  eventType: EVENT_TYPE.TRACK,
54
55
  attributes: {
56
+ // keep nodeName from copied slice
55
57
  nodeType: contentMoved === null || contentMoved === void 0 ? void 0 : contentMoved.nodeName,
56
58
  nodeDepth: contentMoved === null || contentMoved === void 0 ? void 0 : contentMoved.nodeDepth,
57
- destinationNodeDepth: getParentNodeDepth(state.selection)
58
-
59
- // keep nodeName from copied slice
59
+ destinationNodeDepth: getParentNodeDepth(state.selection),
60
+ nodeTypes: contentMoved === null || contentMoved === void 0 ? void 0 : contentMoved.nodeTypes,
61
+ hasSelectedMultipleNodes: contentMoved === null || contentMoved === void 0 ? void 0 : contentMoved.hasSelectedMultipleNodes
60
62
  }
61
63
  })(tr);
62
64
 
@@ -78,6 +80,11 @@ export const createPlugin = (dispatch, editorAnalyticsAPI) => {
78
80
  content,
79
81
  size
80
82
  } = slice;
83
+ const {
84
+ selection
85
+ } = state;
86
+ // Note: the following is not the case once `platform_editor_element_drag_and_drop_multiselect` is enabled
87
+ // we now want to track cut events for multiple nodes
81
88
  // Content should be just one node, so we added a check for slice.content.childCount === 1;
82
89
  // 1. It is possible to select a table by dragging the mouse over the table's rows.
83
90
  // As a result, slice will contain rows without tableNode itself and the childCount will be the number of rows.
@@ -86,37 +93,58 @@ export const createPlugin = (dispatch, editorAnalyticsAPI) => {
86
93
  // the paragraph below the node. Visually only the node in between is selected, in reality, three nodes are
87
94
  // in the slice.
88
95
  // These cases are ignored and moveContent event won't be counted.
89
- if (content.childCount !== 1) {
90
- resetState = true;
91
- }
96
+ const isMultiSelectEnabled = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true);
92
97
  const nodeName = ((_content$firstChild2 = content.firstChild) === null || _content$firstChild2 === void 0 ? void 0 : _content$firstChild2.type.name) || '';
93
-
94
- // Some nodes are not relevant as they are parts of nodes, not whole nodes (like tableCell, tableHeader instead of table node)
95
- // Some nodes like lists, taskList(item), decisionList(item) requires tricky checks that we want to avoid doing.
96
- // These nodes were added to excludedNodes array.
97
- if (!resetState && isExcludedNode(nodeName)) {
98
- resetState = true;
99
- }
100
- const {
101
- selection
102
- } = state;
103
- if (!resetState && !isEntireNestedParagraphOrHeadingSelected(selection)) {
104
- resetState = true;
105
- }
106
- if (!resetState && isInlineNode(nodeName) && isNestedInlineNode(selection)) {
107
- resetState = true;
108
- }
109
- if (!resetState && isNestedInTable(state)) {
98
+ let nodeTypes,
99
+ hasSelectedMultipleNodes = false;
100
+ if (content.childCount > 1) {
101
+ if (isMultiSelectEnabled) {
102
+ if (containsExcludedNode(content)) {
103
+ resetState = true;
104
+ } else {
105
+ const attributes = getMultipleSelectionAttributes(content);
106
+ nodeTypes = attributes.nodeTypes;
107
+ hasSelectedMultipleNodes = attributes.hasSelectedMultipleNodes;
108
+ }
109
+ } else {
110
+ resetState = true;
111
+ }
112
+ } else if (content.childCount === 1) {
113
+ // Some nodes are not relevant as they are parts of nodes, not whole nodes (like tableCell, tableHeader instead of table node)
114
+ // Some nodes like lists, taskList(item), decisionList(item) requires tricky checks that we want to avoid doing.
115
+ // These nodes were added to excludedNodes array.
116
+ if (!resetState && isExcludedNode(nodeName)) {
117
+ resetState = true;
118
+ }
119
+ if (!resetState && !isEntireNestedParagraphOrHeadingSelected(selection)) {
120
+ resetState = true;
121
+ }
122
+ if (!resetState && isInlineNode(nodeName) && isNestedInlineNode(selection)) {
123
+ resetState = true;
124
+ }
125
+ if (!resetState && isNestedInTable(state)) {
126
+ resetState = true;
127
+ }
128
+ } else {
110
129
  resetState = true;
111
130
  }
112
131
  if (resetState) {
113
132
  resetContentMoved()(state, dispatch);
114
133
  } else {
115
- updateContentMoved({
134
+ let newState = {
116
135
  size: size,
117
136
  nodeName: nodeName,
118
137
  nodeDepth: getParentNodeDepth(selection)
119
- }, 'contentCut')(state, dispatch);
138
+ };
139
+ if (isMultiSelectEnabled) {
140
+ var _nodeTypes;
141
+ newState = {
142
+ ...newState,
143
+ nodeTypes: (_nodeTypes = nodeTypes) !== null && _nodeTypes !== void 0 ? _nodeTypes : nodeName,
144
+ hasSelectedMultipleNodes
145
+ };
146
+ }
147
+ updateContentMoved(newState, 'contentCut')(state, dispatch);
120
148
  }
121
149
  isCutEvent = false;
122
150
  return slice;
@@ -9,6 +9,8 @@ export const defaultState = {
9
9
  nodeName: undefined,
10
10
  size: undefined,
11
11
  nodeDepth: undefined,
12
- currentActions: []
12
+ currentActions: [],
13
+ nodeTypes: undefined,
14
+ hasSelectedMultipleNodes: undefined
13
15
  }
14
16
  };
@@ -1,5 +1,6 @@
1
1
  import { findParentNodeClosestToPos, findParentNodeOfTypeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
2
2
  import { CellSelection } from '@atlaskit/editor-tables/cell-selection';
3
+ import { fg } from '@atlaskit/platform-feature-flags';
3
4
  const excludedNodes = ['caption', 'layoutColumn', 'listItem', 'tableHeader', 'tableCell', 'tableRow', 'text', 'placeholder', 'unsupportedBlock', 'unsupportedInline', 'hardBreak', 'confluenceUnsupportedBlock', 'confluenceUnsupportedInline', 'taskItem', 'decisionItem'];
4
5
  export const isExcludedNode = nodeName => excludedNodes.includes(nodeName);
5
6
  export const isCursorSelectionAtTopLevel = selection => {
@@ -67,4 +68,26 @@ export const isEntireNestedParagraphOrHeadingSelected = selection => {
67
68
  $to
68
69
  } = selection;
69
70
  return $from.textOffset === 0 && $to.textOffset === 0;
71
+ };
72
+ export const containsExcludedNode = content => {
73
+ for (let i = 0; i < content.childCount; i++) {
74
+ var _content$maybeChild;
75
+ const nodeName = ((_content$maybeChild = content.maybeChild(i)) === null || _content$maybeChild === void 0 ? void 0 : _content$maybeChild.type.name) || '';
76
+ if (isExcludedNode(nodeName)) {
77
+ return true;
78
+ }
79
+ }
80
+ return false;
81
+ };
82
+ export const getMultipleSelectionAttributes = content => {
83
+ const nodeTypes = [];
84
+ if (content.size) {
85
+ content.forEach(node => {
86
+ nodeTypes.push(node.type.name);
87
+ });
88
+ }
89
+ return {
90
+ nodeTypes: fg('platform_editor_track_node_types') ? [...new Set(nodeTypes)].sort().join(',') : undefined,
91
+ hasSelectedMultipleNodes: nodeTypes.length > 1
92
+ };
70
93
  };
@@ -10,7 +10,9 @@ export var updateContentMoved = function updateContentMoved(nextState, nextActio
10
10
  currentActions: [].concat(_toConsumableArray(contentMoved.currentActions), [nextAction]),
11
11
  size: (nextState === null || nextState === void 0 ? void 0 : nextState.size) || contentMoved.size,
12
12
  nodeName: nextState === null || nextState === void 0 ? void 0 : nextState.nodeName,
13
- nodeDepth: nextState === null || nextState === void 0 ? void 0 : nextState.nodeDepth
13
+ nodeDepth: nextState === null || nextState === void 0 ? void 0 : nextState.nodeDepth,
14
+ nodeTypes: nextState === null || nextState === void 0 ? void 0 : nextState.nodeTypes,
15
+ hasSelectedMultipleNodes: nextState === null || nextState === void 0 ? void 0 : nextState.hasSelectedMultipleNodes
14
16
  };
15
17
  return {
16
18
  type: MoveAnalyticPluginTypes.UpdateMovedAction,
@@ -1,10 +1,14 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ 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; }
3
+ 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; }
1
4
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
2
5
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
6
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
3
7
  import { resetContentMoved, resetContentMovedTransform, updateContentMoved } from './commands';
4
8
  import { createPluginState, getPluginState } from './plugin-factory';
5
9
  import { pluginKey } from './plugin-key';
6
10
  import { defaultState } from './types';
7
- import { getParentNodeDepth, isCursorSelectionAtTopLevel, isEntireNestedParagraphOrHeadingSelected, isExcludedNode, isInlineNode, isNestedInlineNode, isNestedInTable } from './utils';
11
+ import { containsExcludedNode, getMultipleSelectionAttributes, getParentNodeDepth, isCursorSelectionAtTopLevel, isEntireNestedParagraphOrHeadingSelected, isExcludedNode, isInlineNode, isNestedInlineNode, isNestedInTable } from './utils';
8
12
 
9
13
  // This plugin exists only in FullPage/FullWidth Editor and is used to register an event that tells us
10
14
  // that a user cut and than pasted a node. This order of actions could be considered an alternative
@@ -46,11 +50,12 @@ export var createPlugin = function createPlugin(dispatch, editorAnalyticsAPI) {
46
50
  actionSubjectId: ACTION_SUBJECT_ID.NODE,
47
51
  eventType: EVENT_TYPE.TRACK,
48
52
  attributes: {
53
+ // keep nodeName from copied slice
49
54
  nodeType: contentMoved === null || contentMoved === void 0 ? void 0 : contentMoved.nodeName,
50
55
  nodeDepth: contentMoved === null || contentMoved === void 0 ? void 0 : contentMoved.nodeDepth,
51
- destinationNodeDepth: getParentNodeDepth(state.selection)
52
-
53
- // keep nodeName from copied slice
56
+ destinationNodeDepth: getParentNodeDepth(state.selection),
57
+ nodeTypes: contentMoved === null || contentMoved === void 0 ? void 0 : contentMoved.nodeTypes,
58
+ hasSelectedMultipleNodes: contentMoved === null || contentMoved === void 0 ? void 0 : contentMoved.hasSelectedMultipleNodes
54
59
  }
55
60
  })(tr);
56
61
 
@@ -69,6 +74,9 @@ export var createPlugin = function createPlugin(dispatch, editorAnalyticsAPI) {
69
74
  var resetState = false;
70
75
  var content = slice.content,
71
76
  size = slice.size;
77
+ var selection = state.selection;
78
+ // Note: the following is not the case once `platform_editor_element_drag_and_drop_multiselect` is enabled
79
+ // we now want to track cut events for multiple nodes
72
80
  // Content should be just one node, so we added a check for slice.content.childCount === 1;
73
81
  // 1. It is possible to select a table by dragging the mouse over the table's rows.
74
82
  // As a result, slice will contain rows without tableNode itself and the childCount will be the number of rows.
@@ -77,35 +85,57 @@ export var createPlugin = function createPlugin(dispatch, editorAnalyticsAPI) {
77
85
  // the paragraph below the node. Visually only the node in between is selected, in reality, three nodes are
78
86
  // in the slice.
79
87
  // These cases are ignored and moveContent event won't be counted.
80
- if (content.childCount !== 1) {
81
- resetState = true;
82
- }
88
+ var isMultiSelectEnabled = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true);
83
89
  var nodeName = ((_content$firstChild2 = content.firstChild) === null || _content$firstChild2 === void 0 ? void 0 : _content$firstChild2.type.name) || '';
84
-
85
- // Some nodes are not relevant as they are parts of nodes, not whole nodes (like tableCell, tableHeader instead of table node)
86
- // Some nodes like lists, taskList(item), decisionList(item) requires tricky checks that we want to avoid doing.
87
- // These nodes were added to excludedNodes array.
88
- if (!resetState && isExcludedNode(nodeName)) {
89
- resetState = true;
90
- }
91
- var selection = state.selection;
92
- if (!resetState && !isEntireNestedParagraphOrHeadingSelected(selection)) {
93
- resetState = true;
94
- }
95
- if (!resetState && isInlineNode(nodeName) && isNestedInlineNode(selection)) {
96
- resetState = true;
97
- }
98
- if (!resetState && isNestedInTable(state)) {
90
+ var nodeTypes,
91
+ hasSelectedMultipleNodes = false;
92
+ if (content.childCount > 1) {
93
+ if (isMultiSelectEnabled) {
94
+ if (containsExcludedNode(content)) {
95
+ resetState = true;
96
+ } else {
97
+ var attributes = getMultipleSelectionAttributes(content);
98
+ nodeTypes = attributes.nodeTypes;
99
+ hasSelectedMultipleNodes = attributes.hasSelectedMultipleNodes;
100
+ }
101
+ } else {
102
+ resetState = true;
103
+ }
104
+ } else if (content.childCount === 1) {
105
+ // Some nodes are not relevant as they are parts of nodes, not whole nodes (like tableCell, tableHeader instead of table node)
106
+ // Some nodes like lists, taskList(item), decisionList(item) requires tricky checks that we want to avoid doing.
107
+ // These nodes were added to excludedNodes array.
108
+ if (!resetState && isExcludedNode(nodeName)) {
109
+ resetState = true;
110
+ }
111
+ if (!resetState && !isEntireNestedParagraphOrHeadingSelected(selection)) {
112
+ resetState = true;
113
+ }
114
+ if (!resetState && isInlineNode(nodeName) && isNestedInlineNode(selection)) {
115
+ resetState = true;
116
+ }
117
+ if (!resetState && isNestedInTable(state)) {
118
+ resetState = true;
119
+ }
120
+ } else {
99
121
  resetState = true;
100
122
  }
101
123
  if (resetState) {
102
124
  resetContentMoved()(state, dispatch);
103
125
  } else {
104
- updateContentMoved({
126
+ var newState = {
105
127
  size: size,
106
128
  nodeName: nodeName,
107
129
  nodeDepth: getParentNodeDepth(selection)
108
- }, 'contentCut')(state, dispatch);
130
+ };
131
+ if (isMultiSelectEnabled) {
132
+ var _nodeTypes;
133
+ newState = _objectSpread(_objectSpread({}, newState), {}, {
134
+ nodeTypes: (_nodeTypes = nodeTypes) !== null && _nodeTypes !== void 0 ? _nodeTypes : nodeName,
135
+ hasSelectedMultipleNodes: hasSelectedMultipleNodes
136
+ });
137
+ }
138
+ updateContentMoved(newState, 'contentCut')(state, dispatch);
109
139
  }
110
140
  isCutEvent = false;
111
141
  return slice;
@@ -9,6 +9,8 @@ export var defaultState = {
9
9
  nodeName: undefined,
10
10
  size: undefined,
11
11
  nodeDepth: undefined,
12
- currentActions: []
12
+ currentActions: [],
13
+ nodeTypes: undefined,
14
+ hasSelectedMultipleNodes: undefined
13
15
  }
14
16
  };
@@ -1,5 +1,7 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
1
2
  import { findParentNodeClosestToPos, findParentNodeOfTypeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
2
3
  import { CellSelection } from '@atlaskit/editor-tables/cell-selection';
4
+ import { fg } from '@atlaskit/platform-feature-flags';
3
5
  var excludedNodes = ['caption', 'layoutColumn', 'listItem', 'tableHeader', 'tableCell', 'tableRow', 'text', 'placeholder', 'unsupportedBlock', 'unsupportedInline', 'hardBreak', 'confluenceUnsupportedBlock', 'confluenceUnsupportedInline', 'taskItem', 'decisionItem'];
4
6
  export var isExcludedNode = function isExcludedNode(nodeName) {
5
7
  return excludedNodes.includes(nodeName);
@@ -63,4 +65,26 @@ export var isEntireNestedParagraphOrHeadingSelected = function isEntireNestedPar
63
65
  var $from = selection.$from,
64
66
  $to = selection.$to;
65
67
  return $from.textOffset === 0 && $to.textOffset === 0;
68
+ };
69
+ export var containsExcludedNode = function containsExcludedNode(content) {
70
+ for (var i = 0; i < content.childCount; i++) {
71
+ var _content$maybeChild;
72
+ var nodeName = ((_content$maybeChild = content.maybeChild(i)) === null || _content$maybeChild === void 0 ? void 0 : _content$maybeChild.type.name) || '';
73
+ if (isExcludedNode(nodeName)) {
74
+ return true;
75
+ }
76
+ }
77
+ return false;
78
+ };
79
+ export var getMultipleSelectionAttributes = function getMultipleSelectionAttributes(content) {
80
+ var nodeTypes = [];
81
+ if (content.size) {
82
+ content.forEach(function (node) {
83
+ nodeTypes.push(node.type.name);
84
+ });
85
+ }
86
+ return {
87
+ nodeTypes: fg('platform_editor_track_node_types') ? _toConsumableArray(new Set(nodeTypes)).sort().join(',') : undefined,
88
+ hasSelectedMultipleNodes: nodeTypes.length > 1
89
+ };
66
90
  };
@@ -5,6 +5,8 @@ export type ContentMoved = {
5
5
  size?: number;
6
6
  currentActions: Array<ActionType>;
7
7
  nodeDepth?: number;
8
+ nodeTypes?: string;
9
+ hasSelectedMultipleNodes?: boolean;
8
10
  };
9
11
  export type MoveAnalyticsPluginState = {
10
12
  contentMoved: ContentMoved;
@@ -1,3 +1,4 @@
1
+ import type { Fragment } from '@atlaskit/editor-prosemirror/model';
1
2
  import type { EditorState, Selection } from '@atlaskit/editor-prosemirror/state';
2
3
  export declare const isExcludedNode: (nodeName: string) => boolean;
3
4
  export declare const isCursorSelectionAtTopLevel: (selection: Selection) => boolean;
@@ -6,3 +7,8 @@ export declare const isNestedInlineNode: (selection: Selection) => boolean;
6
7
  export declare const isNestedInTable: (state: EditorState) => boolean;
7
8
  export declare const getParentNodeDepth: (selection: Selection) => number;
8
9
  export declare const isEntireNestedParagraphOrHeadingSelected: (selection: Selection) => boolean;
10
+ export declare const containsExcludedNode: (content: Fragment) => boolean;
11
+ export declare const getMultipleSelectionAttributes: (content: Fragment) => {
12
+ nodeTypes: string | undefined;
13
+ hasSelectedMultipleNodes: boolean;
14
+ };
@@ -5,6 +5,8 @@ export type ContentMoved = {
5
5
  size?: number;
6
6
  currentActions: Array<ActionType>;
7
7
  nodeDepth?: number;
8
+ nodeTypes?: string;
9
+ hasSelectedMultipleNodes?: boolean;
8
10
  };
9
11
  export type MoveAnalyticsPluginState = {
10
12
  contentMoved: ContentMoved;
@@ -1,3 +1,4 @@
1
+ import type { Fragment } from '@atlaskit/editor-prosemirror/model';
1
2
  import type { EditorState, Selection } from '@atlaskit/editor-prosemirror/state';
2
3
  export declare const isExcludedNode: (nodeName: string) => boolean;
3
4
  export declare const isCursorSelectionAtTopLevel: (selection: Selection) => boolean;
@@ -6,3 +7,8 @@ export declare const isNestedInlineNode: (selection: Selection) => boolean;
6
7
  export declare const isNestedInTable: (state: EditorState) => boolean;
7
8
  export declare const getParentNodeDepth: (selection: Selection) => number;
8
9
  export declare const isEntireNestedParagraphOrHeadingSelected: (selection: Selection) => boolean;
10
+ export declare const containsExcludedNode: (content: Fragment) => boolean;
11
+ export declare const getMultipleSelectionAttributes: (content: Fragment) => {
12
+ nodeTypes: string | undefined;
13
+ hasSelectedMultipleNodes: boolean;
14
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-paste",
3
- "version": "2.3.0",
3
+ "version": "3.0.0",
4
4
  "description": "Paste plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -32,36 +32,36 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "@atlaskit/adf-schema": "^47.2.1",
35
- "@atlaskit/code": "^15.6.9",
36
- "@atlaskit/editor-common": "^99.16.0",
35
+ "@atlaskit/code": "^16.0.0",
36
+ "@atlaskit/editor-common": "^100.0.0",
37
37
  "@atlaskit/editor-markdown-transformer": "^5.15.0",
38
- "@atlaskit/editor-plugin-analytics": "^1.12.0",
39
- "@atlaskit/editor-plugin-annotation": "^1.28.0",
40
- "@atlaskit/editor-plugin-better-type-history": "^1.11.0",
41
- "@atlaskit/editor-plugin-card": "^4.6.0",
38
+ "@atlaskit/editor-plugin-analytics": "^2.0.0",
39
+ "@atlaskit/editor-plugin-annotation": "^2.0.0",
40
+ "@atlaskit/editor-plugin-better-type-history": "^2.0.0",
41
+ "@atlaskit/editor-plugin-card": "^5.0.0",
42
42
  "@atlaskit/editor-plugin-feature-flags": "^1.3.0",
43
- "@atlaskit/editor-plugin-list": "^3.10.0",
44
- "@atlaskit/editor-plugin-media": "^1.45.0",
45
- "@atlaskit/editor-plugin-mentions": "^2.15.0",
43
+ "@atlaskit/editor-plugin-list": "^4.0.0",
44
+ "@atlaskit/editor-plugin-media": "^2.0.0",
45
+ "@atlaskit/editor-plugin-mentions": "^3.0.0",
46
46
  "@atlaskit/editor-prosemirror": "7.0.0",
47
47
  "@atlaskit/editor-tables": "^2.9.0",
48
- "@atlaskit/media-client": "^31.1.0",
49
- "@atlaskit/media-common": "^11.8.0",
50
- "@atlaskit/platform-feature-flags": "^1.0.0",
51
- "@atlaskit/tmp-editor-statsig": "^2.46.0",
48
+ "@atlaskit/media-client": "^32.0.0",
49
+ "@atlaskit/media-common": "^12.0.0",
50
+ "@atlaskit/platform-feature-flags": "^1.1.0",
51
+ "@atlaskit/tmp-editor-statsig": "^3.0.0",
52
52
  "@babel/runtime": "^7.0.0",
53
53
  "lodash": "^4.17.21",
54
54
  "uuid": "^3.1.0"
55
55
  },
56
56
  "peerDependencies": {
57
- "react": "^16.8.0 || ^17.0.0 || ~18.2.0",
58
- "react-dom": "^16.8.0 || ^17.0.0 || ^18.2.0"
57
+ "react": "^18.2.0",
58
+ "react-dom": "^18.2.0"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@af/visual-regression": "*",
62
- "@atlaskit/editor-plugin-block-type": "^4.3.0",
63
- "@atlaskit/editor-plugin-history": "^1.4.0",
64
- "@atlaskit/editor-plugin-type-ahead": "^1.13.0",
62
+ "@atlaskit/editor-plugin-block-type": "^5.0.0",
63
+ "@atlaskit/editor-plugin-history": "^2.0.0",
64
+ "@atlaskit/editor-plugin-type-ahead": "^2.0.0",
65
65
  "@atlaskit/ssr": "*",
66
66
  "@atlaskit/visual-regression": "*",
67
67
  "@testing-library/react": "^13.4.0",
@@ -137,6 +137,9 @@
137
137
  },
138
138
  "platform_editor_legacy_content_macro_insert": {
139
139
  "type": "boolean"
140
+ },
141
+ "platform_editor_track_node_types": {
142
+ "type": "boolean"
140
143
  }
141
144
  }
142
145
  }