@atlaskit/editor-plugin-block-menu 4.0.1 → 4.0.3

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,22 @@
1
1
  # @atlaskit/editor-plugin-block-menu
2
2
 
3
+ ## 4.0.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`c158b1ba4f0fd`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/c158b1ba4f0fd) -
8
+ ED-29388: fix converting empty list
9
+ - Updated dependencies
10
+
11
+ ## 4.0.2
12
+
13
+ ### Patch Changes
14
+
15
+ - [`9cf29da7572b3`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/9cf29da7572b3) -
16
+ [ux] Fires Element converted event when Turn into menu is used to transform a node from one node
17
+ type to another.
18
+ - Updated dependencies
19
+
3
20
  ## 4.0.1
4
21
 
5
22
  ### Patch Changes
@@ -23,6 +23,9 @@
23
23
  {
24
24
  "path": "../../../design-system/dropdown-menu/afm-cc/tsconfig.json"
25
25
  },
26
+ {
27
+ "path": "../../editor-plugin-analytics/afm-cc/tsconfig.json"
28
+ },
26
29
  {
27
30
  "path": "../../editor-plugin-block-controls/afm-cc/tsconfig.json"
28
31
  },
@@ -23,6 +23,9 @@
23
23
  {
24
24
  "path": "../../../design-system/dropdown-menu/afm-dev-agents/tsconfig.json"
25
25
  },
26
+ {
27
+ "path": "../../editor-plugin-analytics/afm-dev-agents/tsconfig.json"
28
+ },
26
29
  {
27
30
  "path": "../../editor-plugin-block-controls/afm-dev-agents/tsconfig.json"
28
31
  },
@@ -23,6 +23,9 @@
23
23
  {
24
24
  "path": "../../../design-system/dropdown-menu/afm-jira/tsconfig.json"
25
25
  },
26
+ {
27
+ "path": "../../editor-plugin-analytics/afm-jira/tsconfig.json"
28
+ },
26
29
  {
27
30
  "path": "../../editor-plugin-block-controls/afm-jira/tsconfig.json"
28
31
  },
@@ -23,6 +23,9 @@
23
23
  {
24
24
  "path": "../../../design-system/dropdown-menu/afm-passionfruit/tsconfig.json"
25
25
  },
26
+ {
27
+ "path": "../../editor-plugin-analytics/afm-passionfruit/tsconfig.json"
28
+ },
26
29
  {
27
30
  "path": "../../editor-plugin-block-controls/afm-passionfruit/tsconfig.json"
28
31
  },
@@ -23,6 +23,9 @@
23
23
  {
24
24
  "path": "../../../design-system/dropdown-menu/afm-post-office/tsconfig.json"
25
25
  },
26
+ {
27
+ "path": "../../editor-plugin-analytics/afm-post-office/tsconfig.json"
28
+ },
26
29
  {
27
30
  "path": "../../editor-plugin-block-controls/afm-post-office/tsconfig.json"
28
31
  },
@@ -23,6 +23,9 @@
23
23
  {
24
24
  "path": "../../../design-system/dropdown-menu/afm-rovo-extension/tsconfig.json"
25
25
  },
26
+ {
27
+ "path": "../../editor-plugin-analytics/afm-rovo-extension/tsconfig.json"
28
+ },
26
29
  {
27
30
  "path": "../../editor-plugin-block-controls/afm-rovo-extension/tsconfig.json"
28
31
  },
@@ -23,6 +23,9 @@
23
23
  {
24
24
  "path": "../../../design-system/dropdown-menu/afm-townsquare/tsconfig.json"
25
25
  },
26
+ {
27
+ "path": "../../editor-plugin-analytics/afm-townsquare/tsconfig.json"
28
+ },
26
29
  {
27
30
  "path": "../../editor-plugin-block-controls/afm-townsquare/tsconfig.json"
28
31
  },
@@ -38,7 +38,7 @@ var blockMenuPlugin = exports.blockMenuPlugin = function blockMenuPlugin(_ref) {
38
38
  },
39
39
  commands: {
40
40
  formatNode: function formatNode(targetType) {
41
- return (0, _formatNode2.formatNode)(targetType);
41
+ return (0, _formatNode2.formatNode)(api)(targetType);
42
42
  }
43
43
  },
44
44
  getSharedState: function getSharedState(editorState) {
@@ -3,13 +3,16 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.formatNode = void 0;
6
+ exports.formatNodeSelectEmptyList = exports.formatNode = void 0;
7
+ var _analytics = require("@atlaskit/editor-common/analytics");
8
+ var _state = require("@atlaskit/editor-prosemirror/state");
7
9
  var _utils = require("@atlaskit/editor-prosemirror/utils");
8
10
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
9
11
  var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
10
12
  var _selection = require("./selection");
11
13
  var _layoutTransforms = require("./transforms/layout-transforms");
12
14
  var _transformNodeToTargetType = require("./transforms/transformNodeToTargetType");
15
+ var _utils2 = require("./transforms/utils");
13
16
  /**
14
17
  * Handles formatting when selection is empty by inserting a new target node
15
18
  */
@@ -49,64 +52,140 @@ var formatNodeWhenSelectionEmpty = function formatNodeWhenSelectionEmpty(tr, tar
49
52
  return tr;
50
53
  };
51
54
 
55
+ /**
56
+ * Handles formatting when an empty list is selected
57
+ * Converting an empty list to a target node, will remove the list and replace with an empty target node
58
+ */
59
+ var formatNodeSelectEmptyList = exports.formatNodeSelectEmptyList = function formatNodeSelectEmptyList(tr, targetType, listNode, schema) {
60
+ var nodes = schema.nodes;
61
+ var headingLevel = 1;
62
+ var finalTargetType = targetType;
63
+ if (targetType.startsWith('heading')) {
64
+ var levelString = targetType.slice(-1);
65
+ var level = parseInt(levelString, 10);
66
+ if (!isNaN(level) && level >= 1 && level <= 6) {
67
+ headingLevel = level;
68
+ finalTargetType = 'heading';
69
+ }
70
+ }
71
+ var replaceNode = null;
72
+ if (finalTargetType === 'layoutSection') {
73
+ var emptyPara = nodes.paragraph.createAndFill();
74
+ if (emptyPara) {
75
+ replaceNode = (0, _layoutTransforms.createDefaultLayoutSection)(schema, emptyPara);
76
+ }
77
+ } else if (finalTargetType === 'heading') {
78
+ replaceNode = nodes.heading.createAndFill({
79
+ level: headingLevel
80
+ });
81
+ } else {
82
+ replaceNode = nodes[finalTargetType].createAndFill();
83
+ }
84
+ if (replaceNode) {
85
+ tr.replaceWith(listNode.pos, listNode.pos + listNode.node.nodeSize, replaceNode);
86
+ tr.setSelection(new _state.TextSelection(tr.doc.resolve(listNode.pos)));
87
+ }
88
+ return tr;
89
+ };
90
+
52
91
  /**
53
92
  * Formats the current node or selection to the specified target type
93
+ * @param api - The editor API injection that provides access to analytics and other plugin actions
54
94
  * @param targetType - The target node type to convert to
55
95
  */
56
- var formatNode = exports.formatNode = function formatNode(targetType) {
57
- return function (_ref) {
58
- var tr = _ref.tr;
59
- var selection = tr.selection;
60
- var schema = tr.doc.type.schema;
61
- var nodes = schema.nodes;
96
+ var formatNode = exports.formatNode = function formatNode(api) {
97
+ return function (targetType) {
98
+ return function (_ref) {
99
+ var tr = _ref.tr;
100
+ var selection = tr.selection;
101
+ var schema = tr.doc.type.schema;
102
+ var nodes = schema.nodes;
62
103
 
63
- // Find the node to format from the current selection
64
- var nodeToFormat;
65
- var nodePos = selection.from;
104
+ // Find the node to format from the current selection
105
+ var nodeToFormat;
106
+ var nodePos = selection.from;
66
107
 
67
- // when selection is empty, we insert a empty target node
68
- if (selection.empty && (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_block_menu_empty_line', 'isEnabled', true)) {
69
- return formatNodeWhenSelectionEmpty(tr, targetType, nodePos, schema);
70
- }
108
+ // when selection is empty, we insert a empty target node
109
+ if (selection.empty && (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_block_menu_empty_line', 'isEnabled', true)) {
110
+ var listNodes = [];
111
+ // need to find if there is any list node in the current selection
112
+ // As when select a empty list, selection is empty, but we want to convert the list instead of inserting a target node
113
+ // findSelectedNodeOfType does not work when selection is empty, so we use nodesBetween
114
+ tr.doc.nodesBetween(selection.from, selection.to, function (node, pos) {
115
+ if ((0, _utils2.isListNodeType)(node.type)) {
116
+ listNodes.push({
117
+ node: node,
118
+ pos: pos
119
+ });
120
+ }
121
+ });
122
+ // get the first list node as when click on drag handle if there are list node
123
+ // can only select one list at a time, so we just need to find the first one
124
+ if (listNodes.length > 0 && (0, _platformFeatureFlags.fg)('platform_editor_block_menu_patch_2')) {
125
+ var firstChild = listNodes[0];
126
+ return formatNodeSelectEmptyList(tr, targetType, firstChild, schema);
127
+ } else {
128
+ return formatNodeWhenSelectionEmpty(tr, targetType, nodePos, schema);
129
+ }
130
+ }
71
131
 
72
- // Try to find the current node from selection
73
- var selectedNode = (0, _utils.findSelectedNodeOfType)([nodes.paragraph, nodes.heading, nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.bulletList, nodes.orderedList, nodes.taskList, nodes.layoutSection])(selection);
74
- if (selectedNode) {
75
- nodeToFormat = selectedNode.node;
76
- nodePos = selectedNode.pos;
77
- } else {
78
- // Try to find parent node (including list parents)
79
- var parentNode = (0, _utils.findParentNodeOfType)([nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.listItem, nodes.taskItem, nodes.layoutSection])(selection);
80
- if (parentNode) {
81
- nodeToFormat = parentNode.node;
82
- nodePos = parentNode.pos;
83
- var paragraphOrHeadingNode = (0, _utils.findParentNodeOfType)([nodes.paragraph, nodes.heading])(selection);
84
- // Special case: if we found a listItem, check if we need the parent list instead
85
- if (parentNode.node.type === nodes.listItem || parentNode.node.type === nodes.taskItem) {
86
- var listParent = (0, _utils.findParentNodeOfType)([nodes.bulletList, nodes.orderedList, nodes.taskList])(selection);
87
- if (listParent) {
88
- // For list transformations, we want the list parent, not the listItem
89
- nodeToFormat = listParent.node;
90
- nodePos = listParent.pos;
132
+ // Try to find the current node from selection
133
+ var selectedNode = (0, _utils.findSelectedNodeOfType)([nodes.paragraph, nodes.heading, nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.bulletList, nodes.orderedList, nodes.taskList, nodes.layoutSection])(selection);
134
+ if (selectedNode) {
135
+ nodeToFormat = selectedNode.node;
136
+ nodePos = selectedNode.pos;
137
+ } else {
138
+ // Try to find parent node (including list parents)
139
+ var parentNode = (0, _utils.findParentNodeOfType)([nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.listItem, nodes.taskItem, nodes.layoutSection])(selection);
140
+ if (parentNode) {
141
+ nodeToFormat = parentNode.node;
142
+ nodePos = parentNode.pos;
143
+ var paragraphOrHeadingNode = (0, _utils.findParentNodeOfType)([nodes.paragraph, nodes.heading])(selection);
144
+ // Special case: if we found a listItem, check if we need the parent list instead
145
+ if (parentNode.node.type === nodes.listItem || parentNode.node.type === nodes.taskItem) {
146
+ var listParent = (0, _utils.findParentNodeOfType)([nodes.bulletList, nodes.orderedList, nodes.taskList])(selection);
147
+ if (listParent) {
148
+ // For list transformations, we want the list parent, not the listItem
149
+ nodeToFormat = listParent.node;
150
+ nodePos = listParent.pos;
151
+ }
152
+ } else if (parentNode.node.type !== nodes.blockquote && paragraphOrHeadingNode) {
153
+ nodeToFormat = paragraphOrHeadingNode.node;
154
+ nodePos = paragraphOrHeadingNode.pos;
91
155
  }
92
- } else if (parentNode.node.type !== nodes.blockquote && paragraphOrHeadingNode) {
93
- nodeToFormat = paragraphOrHeadingNode.node;
94
- nodePos = paragraphOrHeadingNode.pos;
95
156
  }
96
157
  }
97
- }
98
- if (!nodeToFormat) {
99
- nodeToFormat = selection.$from.node();
100
- nodePos = selection.$from.pos;
101
- }
102
- try {
103
- var newTr = (0, _transformNodeToTargetType.transformNodeToTargetType)(tr, nodeToFormat, nodePos, targetType);
104
- if (newTr && (0, _platformFeatureFlags.fg)('platform_editor_block_menu_selection_fix')) {
105
- return (0, _selection.setSelectionAfterTransform)(newTr, nodePos, targetType);
158
+ if (!nodeToFormat) {
159
+ nodeToFormat = selection.$from.node();
160
+ nodePos = selection.$from.pos;
106
161
  }
107
- return newTr;
108
- } catch (_unused) {
109
- return null;
110
- }
162
+ try {
163
+ var _nodeToFormat$attrs;
164
+ var newTr = (0, _transformNodeToTargetType.transformNodeToTargetType)(tr, nodeToFormat, nodePos, targetType);
165
+ var sourceTypeName = nodeToFormat.type.name;
166
+ if (sourceTypeName === 'heading' && (_nodeToFormat$attrs = nodeToFormat.attrs) !== null && _nodeToFormat$attrs !== void 0 && _nodeToFormat$attrs.level) {
167
+ sourceTypeName = "heading".concat(nodeToFormat.attrs.level);
168
+ }
169
+ if (newTr && sourceTypeName !== targetType) {
170
+ var _api$analytics;
171
+ api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 || _api$analytics.attachAnalyticsEvent({
172
+ action: _analytics.ACTION.CONVERTED,
173
+ actionSubject: _analytics.ACTION_SUBJECT.ELEMENT,
174
+ eventType: _analytics.EVENT_TYPE.TRACK,
175
+ attributes: {
176
+ from: sourceTypeName,
177
+ to: targetType,
178
+ inputMethod: _analytics.INPUT_METHOD.BLOCK_MENU
179
+ }
180
+ })(newTr);
181
+ }
182
+ if (newTr && (0, _platformFeatureFlags.fg)('platform_editor_block_menu_selection_fix')) {
183
+ return (0, _selection.setSelectionAfterTransform)(newTr, nodePos, targetType);
184
+ }
185
+ return newTr;
186
+ } catch (_unused) {
187
+ return null;
188
+ }
189
+ };
111
190
  };
112
191
  };
@@ -32,7 +32,7 @@ export const blockMenuPlugin = ({
32
32
  },
33
33
  commands: {
34
34
  formatNode: targetType => {
35
- return formatNode(targetType);
35
+ return formatNode(api)(targetType);
36
36
  }
37
37
  },
38
38
  getSharedState(editorState) {
@@ -1,9 +1,13 @@
1
+ import { ACTION, ACTION_SUBJECT, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
2
+ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
1
3
  import { findParentNodeOfType, findSelectedNodeOfType, safeInsert as pmSafeInsert } from '@atlaskit/editor-prosemirror/utils';
2
4
  import { fg } from '@atlaskit/platform-feature-flags';
3
5
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
4
6
  import { setSelectionAfterTransform } from './selection';
5
7
  import { createDefaultLayoutSection } from './transforms/layout-transforms';
6
8
  import { transformNodeToTargetType } from './transforms/transformNodeToTargetType';
9
+ import { isListNodeType } from './transforms/utils';
10
+
7
11
  /**
8
12
  * Handles formatting when selection is empty by inserting a new target node
9
13
  */
@@ -47,11 +51,50 @@ const formatNodeWhenSelectionEmpty = (tr, targetType, nodePos, schema) => {
47
51
  return tr;
48
52
  };
49
53
 
54
+ /**
55
+ * Handles formatting when an empty list is selected
56
+ * Converting an empty list to a target node, will remove the list and replace with an empty target node
57
+ */
58
+ export const formatNodeSelectEmptyList = (tr, targetType, listNode, schema) => {
59
+ const {
60
+ nodes
61
+ } = schema;
62
+ let headingLevel = 1;
63
+ let finalTargetType = targetType;
64
+ if (targetType.startsWith('heading')) {
65
+ const levelString = targetType.slice(-1);
66
+ const level = parseInt(levelString, 10);
67
+ if (!isNaN(level) && level >= 1 && level <= 6) {
68
+ headingLevel = level;
69
+ finalTargetType = 'heading';
70
+ }
71
+ }
72
+ let replaceNode = null;
73
+ if (finalTargetType === 'layoutSection') {
74
+ const emptyPara = nodes.paragraph.createAndFill();
75
+ if (emptyPara) {
76
+ replaceNode = createDefaultLayoutSection(schema, emptyPara);
77
+ }
78
+ } else if (finalTargetType === 'heading') {
79
+ replaceNode = nodes.heading.createAndFill({
80
+ level: headingLevel
81
+ });
82
+ } else {
83
+ replaceNode = nodes[finalTargetType].createAndFill();
84
+ }
85
+ if (replaceNode) {
86
+ tr.replaceWith(listNode.pos, listNode.pos + listNode.node.nodeSize, replaceNode);
87
+ tr.setSelection(new TextSelection(tr.doc.resolve(listNode.pos)));
88
+ }
89
+ return tr;
90
+ };
91
+
50
92
  /**
51
93
  * Formats the current node or selection to the specified target type
94
+ * @param api - The editor API injection that provides access to analytics and other plugin actions
52
95
  * @param targetType - The target node type to convert to
53
96
  */
54
- export const formatNode = targetType => {
97
+ export const formatNode = api => targetType => {
55
98
  return ({
56
99
  tr
57
100
  }) => {
@@ -69,7 +112,26 @@ export const formatNode = targetType => {
69
112
 
70
113
  // when selection is empty, we insert a empty target node
71
114
  if (selection.empty && expValEqualsNoExposure('platform_editor_block_menu_empty_line', 'isEnabled', true)) {
72
- return formatNodeWhenSelectionEmpty(tr, targetType, nodePos, schema);
115
+ const listNodes = [];
116
+ // need to find if there is any list node in the current selection
117
+ // As when select a empty list, selection is empty, but we want to convert the list instead of inserting a target node
118
+ // findSelectedNodeOfType does not work when selection is empty, so we use nodesBetween
119
+ tr.doc.nodesBetween(selection.from, selection.to, (node, pos) => {
120
+ if (isListNodeType(node.type)) {
121
+ listNodes.push({
122
+ node,
123
+ pos
124
+ });
125
+ }
126
+ });
127
+ // get the first list node as when click on drag handle if there are list node
128
+ // can only select one list at a time, so we just need to find the first one
129
+ if (listNodes.length > 0 && fg('platform_editor_block_menu_patch_2')) {
130
+ const firstChild = listNodes[0];
131
+ return formatNodeSelectEmptyList(tr, targetType, firstChild, schema);
132
+ } else {
133
+ return formatNodeWhenSelectionEmpty(tr, targetType, nodePos, schema);
134
+ }
73
135
  }
74
136
 
75
137
  // Try to find the current node from selection
@@ -103,7 +165,25 @@ export const formatNode = targetType => {
103
165
  nodePos = selection.$from.pos;
104
166
  }
105
167
  try {
168
+ var _nodeToFormat$attrs;
106
169
  const newTr = transformNodeToTargetType(tr, nodeToFormat, nodePos, targetType);
170
+ let sourceTypeName = nodeToFormat.type.name;
171
+ if (sourceTypeName === 'heading' && (_nodeToFormat$attrs = nodeToFormat.attrs) !== null && _nodeToFormat$attrs !== void 0 && _nodeToFormat$attrs.level) {
172
+ sourceTypeName = `heading${nodeToFormat.attrs.level}`;
173
+ }
174
+ if (newTr && sourceTypeName !== targetType) {
175
+ var _api$analytics, _api$analytics$action;
176
+ 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({
177
+ action: ACTION.CONVERTED,
178
+ actionSubject: ACTION_SUBJECT.ELEMENT,
179
+ eventType: EVENT_TYPE.TRACK,
180
+ attributes: {
181
+ from: sourceTypeName,
182
+ to: targetType,
183
+ inputMethod: INPUT_METHOD.BLOCK_MENU
184
+ }
185
+ })(newTr);
186
+ }
107
187
  if (newTr && fg('platform_editor_block_menu_selection_fix')) {
108
188
  return setSelectionAfterTransform(newTr, nodePos, targetType);
109
189
  }
@@ -31,7 +31,7 @@ export var blockMenuPlugin = function blockMenuPlugin(_ref) {
31
31
  },
32
32
  commands: {
33
33
  formatNode: function formatNode(targetType) {
34
- return _formatNode(targetType);
34
+ return _formatNode(api)(targetType);
35
35
  }
36
36
  },
37
37
  getSharedState: function getSharedState(editorState) {
@@ -1,9 +1,13 @@
1
+ import { ACTION, ACTION_SUBJECT, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
2
+ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
1
3
  import { findParentNodeOfType, findSelectedNodeOfType, safeInsert as pmSafeInsert } from '@atlaskit/editor-prosemirror/utils';
2
4
  import { fg } from '@atlaskit/platform-feature-flags';
3
5
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
4
6
  import { setSelectionAfterTransform } from './selection';
5
7
  import { createDefaultLayoutSection } from './transforms/layout-transforms';
6
8
  import { transformNodeToTargetType } from './transforms/transformNodeToTargetType';
9
+ import { isListNodeType } from './transforms/utils';
10
+
7
11
  /**
8
12
  * Handles formatting when selection is empty by inserting a new target node
9
13
  */
@@ -43,64 +47,140 @@ var formatNodeWhenSelectionEmpty = function formatNodeWhenSelectionEmpty(tr, tar
43
47
  return tr;
44
48
  };
45
49
 
50
+ /**
51
+ * Handles formatting when an empty list is selected
52
+ * Converting an empty list to a target node, will remove the list and replace with an empty target node
53
+ */
54
+ export var formatNodeSelectEmptyList = function formatNodeSelectEmptyList(tr, targetType, listNode, schema) {
55
+ var nodes = schema.nodes;
56
+ var headingLevel = 1;
57
+ var finalTargetType = targetType;
58
+ if (targetType.startsWith('heading')) {
59
+ var levelString = targetType.slice(-1);
60
+ var level = parseInt(levelString, 10);
61
+ if (!isNaN(level) && level >= 1 && level <= 6) {
62
+ headingLevel = level;
63
+ finalTargetType = 'heading';
64
+ }
65
+ }
66
+ var replaceNode = null;
67
+ if (finalTargetType === 'layoutSection') {
68
+ var emptyPara = nodes.paragraph.createAndFill();
69
+ if (emptyPara) {
70
+ replaceNode = createDefaultLayoutSection(schema, emptyPara);
71
+ }
72
+ } else if (finalTargetType === 'heading') {
73
+ replaceNode = nodes.heading.createAndFill({
74
+ level: headingLevel
75
+ });
76
+ } else {
77
+ replaceNode = nodes[finalTargetType].createAndFill();
78
+ }
79
+ if (replaceNode) {
80
+ tr.replaceWith(listNode.pos, listNode.pos + listNode.node.nodeSize, replaceNode);
81
+ tr.setSelection(new TextSelection(tr.doc.resolve(listNode.pos)));
82
+ }
83
+ return tr;
84
+ };
85
+
46
86
  /**
47
87
  * Formats the current node or selection to the specified target type
88
+ * @param api - The editor API injection that provides access to analytics and other plugin actions
48
89
  * @param targetType - The target node type to convert to
49
90
  */
50
- export var formatNode = function formatNode(targetType) {
51
- return function (_ref) {
52
- var tr = _ref.tr;
53
- var selection = tr.selection;
54
- var schema = tr.doc.type.schema;
55
- var nodes = schema.nodes;
91
+ export var formatNode = function formatNode(api) {
92
+ return function (targetType) {
93
+ return function (_ref) {
94
+ var tr = _ref.tr;
95
+ var selection = tr.selection;
96
+ var schema = tr.doc.type.schema;
97
+ var nodes = schema.nodes;
56
98
 
57
- // Find the node to format from the current selection
58
- var nodeToFormat;
59
- var nodePos = selection.from;
99
+ // Find the node to format from the current selection
100
+ var nodeToFormat;
101
+ var nodePos = selection.from;
60
102
 
61
- // when selection is empty, we insert a empty target node
62
- if (selection.empty && expValEqualsNoExposure('platform_editor_block_menu_empty_line', 'isEnabled', true)) {
63
- return formatNodeWhenSelectionEmpty(tr, targetType, nodePos, schema);
64
- }
103
+ // when selection is empty, we insert a empty target node
104
+ if (selection.empty && expValEqualsNoExposure('platform_editor_block_menu_empty_line', 'isEnabled', true)) {
105
+ var listNodes = [];
106
+ // need to find if there is any list node in the current selection
107
+ // As when select a empty list, selection is empty, but we want to convert the list instead of inserting a target node
108
+ // findSelectedNodeOfType does not work when selection is empty, so we use nodesBetween
109
+ tr.doc.nodesBetween(selection.from, selection.to, function (node, pos) {
110
+ if (isListNodeType(node.type)) {
111
+ listNodes.push({
112
+ node: node,
113
+ pos: pos
114
+ });
115
+ }
116
+ });
117
+ // get the first list node as when click on drag handle if there are list node
118
+ // can only select one list at a time, so we just need to find the first one
119
+ if (listNodes.length > 0 && fg('platform_editor_block_menu_patch_2')) {
120
+ var firstChild = listNodes[0];
121
+ return formatNodeSelectEmptyList(tr, targetType, firstChild, schema);
122
+ } else {
123
+ return formatNodeWhenSelectionEmpty(tr, targetType, nodePos, schema);
124
+ }
125
+ }
65
126
 
66
- // Try to find the current node from selection
67
- var selectedNode = findSelectedNodeOfType([nodes.paragraph, nodes.heading, nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.bulletList, nodes.orderedList, nodes.taskList, nodes.layoutSection])(selection);
68
- if (selectedNode) {
69
- nodeToFormat = selectedNode.node;
70
- nodePos = selectedNode.pos;
71
- } else {
72
- // Try to find parent node (including list parents)
73
- var parentNode = findParentNodeOfType([nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.listItem, nodes.taskItem, nodes.layoutSection])(selection);
74
- if (parentNode) {
75
- nodeToFormat = parentNode.node;
76
- nodePos = parentNode.pos;
77
- var paragraphOrHeadingNode = findParentNodeOfType([nodes.paragraph, nodes.heading])(selection);
78
- // Special case: if we found a listItem, check if we need the parent list instead
79
- if (parentNode.node.type === nodes.listItem || parentNode.node.type === nodes.taskItem) {
80
- var listParent = findParentNodeOfType([nodes.bulletList, nodes.orderedList, nodes.taskList])(selection);
81
- if (listParent) {
82
- // For list transformations, we want the list parent, not the listItem
83
- nodeToFormat = listParent.node;
84
- nodePos = listParent.pos;
127
+ // Try to find the current node from selection
128
+ var selectedNode = findSelectedNodeOfType([nodes.paragraph, nodes.heading, nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.bulletList, nodes.orderedList, nodes.taskList, nodes.layoutSection])(selection);
129
+ if (selectedNode) {
130
+ nodeToFormat = selectedNode.node;
131
+ nodePos = selectedNode.pos;
132
+ } else {
133
+ // Try to find parent node (including list parents)
134
+ var parentNode = findParentNodeOfType([nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.listItem, nodes.taskItem, nodes.layoutSection])(selection);
135
+ if (parentNode) {
136
+ nodeToFormat = parentNode.node;
137
+ nodePos = parentNode.pos;
138
+ var paragraphOrHeadingNode = findParentNodeOfType([nodes.paragraph, nodes.heading])(selection);
139
+ // Special case: if we found a listItem, check if we need the parent list instead
140
+ if (parentNode.node.type === nodes.listItem || parentNode.node.type === nodes.taskItem) {
141
+ var listParent = findParentNodeOfType([nodes.bulletList, nodes.orderedList, nodes.taskList])(selection);
142
+ if (listParent) {
143
+ // For list transformations, we want the list parent, not the listItem
144
+ nodeToFormat = listParent.node;
145
+ nodePos = listParent.pos;
146
+ }
147
+ } else if (parentNode.node.type !== nodes.blockquote && paragraphOrHeadingNode) {
148
+ nodeToFormat = paragraphOrHeadingNode.node;
149
+ nodePos = paragraphOrHeadingNode.pos;
85
150
  }
86
- } else if (parentNode.node.type !== nodes.blockquote && paragraphOrHeadingNode) {
87
- nodeToFormat = paragraphOrHeadingNode.node;
88
- nodePos = paragraphOrHeadingNode.pos;
89
151
  }
90
152
  }
91
- }
92
- if (!nodeToFormat) {
93
- nodeToFormat = selection.$from.node();
94
- nodePos = selection.$from.pos;
95
- }
96
- try {
97
- var newTr = transformNodeToTargetType(tr, nodeToFormat, nodePos, targetType);
98
- if (newTr && fg('platform_editor_block_menu_selection_fix')) {
99
- return setSelectionAfterTransform(newTr, nodePos, targetType);
153
+ if (!nodeToFormat) {
154
+ nodeToFormat = selection.$from.node();
155
+ nodePos = selection.$from.pos;
100
156
  }
101
- return newTr;
102
- } catch (_unused) {
103
- return null;
104
- }
157
+ try {
158
+ var _nodeToFormat$attrs;
159
+ var newTr = transformNodeToTargetType(tr, nodeToFormat, nodePos, targetType);
160
+ var sourceTypeName = nodeToFormat.type.name;
161
+ if (sourceTypeName === 'heading' && (_nodeToFormat$attrs = nodeToFormat.attrs) !== null && _nodeToFormat$attrs !== void 0 && _nodeToFormat$attrs.level) {
162
+ sourceTypeName = "heading".concat(nodeToFormat.attrs.level);
163
+ }
164
+ if (newTr && sourceTypeName !== targetType) {
165
+ var _api$analytics;
166
+ api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 || _api$analytics.attachAnalyticsEvent({
167
+ action: ACTION.CONVERTED,
168
+ actionSubject: ACTION_SUBJECT.ELEMENT,
169
+ eventType: EVENT_TYPE.TRACK,
170
+ attributes: {
171
+ from: sourceTypeName,
172
+ to: targetType,
173
+ inputMethod: INPUT_METHOD.BLOCK_MENU
174
+ }
175
+ })(newTr);
176
+ }
177
+ if (newTr && fg('platform_editor_block_menu_selection_fix')) {
178
+ return setSelectionAfterTransform(newTr, nodePos, targetType);
179
+ }
180
+ return newTr;
181
+ } catch (_unused) {
182
+ return null;
183
+ }
184
+ };
105
185
  };
106
186
  };
@@ -1,7 +1,19 @@
1
- import type { EditorCommand } from '@atlaskit/editor-common/types';
1
+ import type { EditorCommand, ExtractInjectionAPI } from '@atlaskit/editor-common/types';
2
+ import { type Schema, type Node as PMNode } from '@atlaskit/editor-prosemirror/model';
3
+ import { type Transaction } from '@atlaskit/editor-prosemirror/state';
4
+ import type { BlockMenuPlugin } from '../blockMenuPluginType';
2
5
  import type { FormatNodeTargetType } from './transforms/types';
6
+ /**
7
+ * Handles formatting when an empty list is selected
8
+ * Converting an empty list to a target node, will remove the list and replace with an empty target node
9
+ */
10
+ export declare const formatNodeSelectEmptyList: (tr: Transaction, targetType: FormatNodeTargetType, listNode: {
11
+ node: PMNode;
12
+ pos: number;
13
+ }, schema: Schema) => Transaction;
3
14
  /**
4
15
  * Formats the current node or selection to the specified target type
16
+ * @param api - The editor API injection that provides access to analytics and other plugin actions
5
17
  * @param targetType - The target node type to convert to
6
18
  */
7
- export declare const formatNode: (targetType: FormatNodeTargetType) => EditorCommand;
19
+ export declare const formatNode: (api?: ExtractInjectionAPI<BlockMenuPlugin>) => (targetType: FormatNodeTargetType) => EditorCommand;
@@ -1,7 +1,19 @@
1
- import type { EditorCommand } from '@atlaskit/editor-common/types';
1
+ import type { EditorCommand, ExtractInjectionAPI } from '@atlaskit/editor-common/types';
2
+ import { type Schema, type Node as PMNode } from '@atlaskit/editor-prosemirror/model';
3
+ import { type Transaction } from '@atlaskit/editor-prosemirror/state';
4
+ import type { BlockMenuPlugin } from '../blockMenuPluginType';
2
5
  import type { FormatNodeTargetType } from './transforms/types';
6
+ /**
7
+ * Handles formatting when an empty list is selected
8
+ * Converting an empty list to a target node, will remove the list and replace with an empty target node
9
+ */
10
+ export declare const formatNodeSelectEmptyList: (tr: Transaction, targetType: FormatNodeTargetType, listNode: {
11
+ node: PMNode;
12
+ pos: number;
13
+ }, schema: Schema) => Transaction;
3
14
  /**
4
15
  * Formats the current node or selection to the specified target type
16
+ * @param api - The editor API injection that provides access to analytics and other plugin actions
5
17
  * @param targetType - The target node type to convert to
6
18
  */
7
- export declare const formatNode: (targetType: FormatNodeTargetType) => EditorCommand;
19
+ export declare const formatNode: (api?: ExtractInjectionAPI<BlockMenuPlugin>) => (targetType: FormatNodeTargetType) => EditorCommand;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-block-menu",
3
- "version": "4.0.1",
3
+ "version": "4.0.3",
4
4
  "description": "BlockMenu plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -30,6 +30,7 @@
30
30
  "dependencies": {
31
31
  "@atlaskit/css": "^0.14.0",
32
32
  "@atlaskit/dropdown-menu": "^16.3.0",
33
+ "@atlaskit/editor-plugin-analytics": "^6.1.0",
33
34
  "@atlaskit/editor-plugin-block-controls": "^7.1.0",
34
35
  "@atlaskit/editor-plugin-decorations": "^6.1.0",
35
36
  "@atlaskit/editor-plugin-selection": "^6.0.0",
@@ -39,15 +40,15 @@
39
40
  "@atlaskit/editor-tables": "^2.9.0",
40
41
  "@atlaskit/editor-toolbar": "^0.10.0",
41
42
  "@atlaskit/icon": "^28.3.0",
42
- "@atlaskit/icon-lab": "^5.7.0",
43
+ "@atlaskit/icon-lab": "^5.8.0",
43
44
  "@atlaskit/platform-feature-flags": "^1.1.0",
44
45
  "@atlaskit/primitives": "^14.15.0",
45
46
  "@atlaskit/tmp-editor-statsig": "^12.32.0",
46
- "@atlaskit/tokens": "^6.3.0",
47
+ "@atlaskit/tokens": "^6.4.0",
47
48
  "@babel/runtime": "^7.0.0"
48
49
  },
49
50
  "peerDependencies": {
50
- "@atlaskit/editor-common": "^110.1.0",
51
+ "@atlaskit/editor-common": "^110.3.0",
51
52
  "react": "^18.2.0",
52
53
  "react-intl-next": "npm:react-intl@^5.18.1"
53
54
  },