@atlaskit/editor-plugin-block-menu 5.2.2 → 5.2.4

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 (45) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/editor-commands/transform-node-utils/{flattenListStep.js → steps/flattenListStep.js} +3 -5
  3. package/dist/cjs/editor-commands/transform-node-utils/steps/listToListStep.js +232 -0
  4. package/dist/cjs/editor-commands/transform-node-utils/{wrapMixedContentStep.js → steps/wrapMixedContentStep.js} +25 -25
  5. package/dist/cjs/editor-commands/transform-node-utils/transform.js +14 -17
  6. package/dist/es2019/editor-commands/transform-node-utils/{flattenListStep.js → steps/flattenListStep.js} +3 -5
  7. package/dist/es2019/editor-commands/transform-node-utils/steps/listToListStep.js +225 -0
  8. package/dist/es2019/editor-commands/transform-node-utils/{wrapMixedContentStep.js → steps/wrapMixedContentStep.js} +25 -22
  9. package/dist/es2019/editor-commands/transform-node-utils/transform.js +14 -17
  10. package/dist/esm/editor-commands/transform-node-utils/{flattenListStep.js → steps/flattenListStep.js} +3 -5
  11. package/dist/esm/editor-commands/transform-node-utils/steps/listToListStep.js +226 -0
  12. package/dist/esm/editor-commands/transform-node-utils/{wrapMixedContentStep.js → steps/wrapMixedContentStep.js} +25 -24
  13. package/dist/esm/editor-commands/transform-node-utils/transform.js +14 -17
  14. package/dist/{types-ts4.5/editor-commands/transform-node-utils → types/editor-commands/transform-node-utils/steps}/flattenListStep.d.ts +1 -1
  15. package/dist/types/editor-commands/transform-node-utils/steps/listToListStep.d.ts +65 -0
  16. package/dist/types/editor-commands/transform-node-utils/{unwrapListStep.d.ts → steps/unwrapListStep.d.ts} +1 -1
  17. package/dist/types/editor-commands/transform-node-utils/{wrapMixedContentStep.d.ts → steps/wrapMixedContentStep.d.ts} +1 -1
  18. package/dist/{types/editor-commands/transform-node-utils → types-ts4.5/editor-commands/transform-node-utils/steps}/flattenListStep.d.ts +1 -1
  19. package/dist/types-ts4.5/editor-commands/transform-node-utils/steps/listToListStep.d.ts +65 -0
  20. package/dist/types-ts4.5/editor-commands/transform-node-utils/{unwrapListStep.d.ts → steps/unwrapListStep.d.ts} +1 -1
  21. package/dist/types-ts4.5/editor-commands/transform-node-utils/{wrapMixedContentStep.d.ts → steps/wrapMixedContentStep.d.ts} +1 -1
  22. package/package.json +1 -1
  23. package/dist/cjs/editor-commands/transform-node-utils/steps/convertBulletListToTextStep.js +0 -34
  24. package/dist/cjs/editor-commands/transform-node-utils/steps/convertOrderedListToTextStep.js +0 -62
  25. package/dist/cjs/editor-commands/transform-node-utils/steps/convertTaskListToTextStep.js +0 -39
  26. package/dist/cjs/editor-commands/transform-node-utils/steps/createListToTextStep.js +0 -90
  27. package/dist/es2019/editor-commands/transform-node-utils/steps/convertBulletListToTextStep.js +0 -27
  28. package/dist/es2019/editor-commands/transform-node-utils/steps/convertOrderedListToTextStep.js +0 -55
  29. package/dist/es2019/editor-commands/transform-node-utils/steps/convertTaskListToTextStep.js +0 -34
  30. package/dist/es2019/editor-commands/transform-node-utils/steps/createListToTextStep.js +0 -86
  31. package/dist/esm/editor-commands/transform-node-utils/steps/convertBulletListToTextStep.js +0 -29
  32. package/dist/esm/editor-commands/transform-node-utils/steps/convertOrderedListToTextStep.js +0 -57
  33. package/dist/esm/editor-commands/transform-node-utils/steps/convertTaskListToTextStep.js +0 -34
  34. package/dist/esm/editor-commands/transform-node-utils/steps/createListToTextStep.js +0 -85
  35. package/dist/types/editor-commands/transform-node-utils/steps/convertBulletListToTextStep.d.ts +0 -18
  36. package/dist/types/editor-commands/transform-node-utils/steps/convertOrderedListToTextStep.d.ts +0 -19
  37. package/dist/types/editor-commands/transform-node-utils/steps/convertTaskListToTextStep.d.ts +0 -22
  38. package/dist/types/editor-commands/transform-node-utils/steps/createListToTextStep.d.ts +0 -38
  39. package/dist/types-ts4.5/editor-commands/transform-node-utils/steps/convertBulletListToTextStep.d.ts +0 -18
  40. package/dist/types-ts4.5/editor-commands/transform-node-utils/steps/convertOrderedListToTextStep.d.ts +0 -19
  41. package/dist/types-ts4.5/editor-commands/transform-node-utils/steps/convertTaskListToTextStep.d.ts +0 -22
  42. package/dist/types-ts4.5/editor-commands/transform-node-utils/steps/createListToTextStep.d.ts +0 -38
  43. /package/dist/cjs/editor-commands/transform-node-utils/{unwrapListStep.js → steps/unwrapListStep.js} +0 -0
  44. /package/dist/es2019/editor-commands/transform-node-utils/{unwrapListStep.js → steps/unwrapListStep.js} +0 -0
  45. /package/dist/esm/editor-commands/transform-node-utils/{unwrapListStep.js → steps/unwrapListStep.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @atlaskit/editor-plugin-block-menu
2
2
 
3
+ ## 5.2.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [`dbbf4fabef4fe`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/dbbf4fabef4fe) -
8
+ [ux] Scope change | Remove text transformations for incompatible nodes
9
+ - [`bb7cbcb12ae4f`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/bb7cbcb12ae4f) -
10
+ [ux] Implement transformation steps for panel to other nodes
11
+
12
+ ## 5.2.3
13
+
14
+ ### Patch Changes
15
+
16
+ - [`d0857f52fd866`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/d0857f52fd866) -
17
+ Add list-to-list transformation support in block menu
18
+
3
19
  ## 5.2.2
4
20
 
5
21
  ### Patch Changes
@@ -20,12 +20,10 @@ var extractNestedLists = function extractNestedLists(node, listTypes, itemTypes,
20
20
  return grandChild.type === type;
21
21
  })) {
22
22
  nestedLists.push(grandChild);
23
+ } else if (grandChild.isText) {
24
+ contentWithoutNestedLists.push(paragraph.createAndFill({}, grandChild));
23
25
  } else {
24
- if (grandChild.isText) {
25
- contentWithoutNestedLists.push(paragraph.createAndFill({}, grandChild));
26
- } else {
27
- contentWithoutNestedLists.push(grandChild);
28
- }
26
+ contentWithoutNestedLists.push(grandChild);
29
27
  }
30
28
  });
31
29
  items.push(child.copy(_model.Fragment.from(contentWithoutNestedLists)));
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.listToListStep = void 0;
7
+ var _model = require("@atlaskit/editor-prosemirror/model");
8
+ var isListType = function isListType(node, schema) {
9
+ var lists = [schema.nodes.taskList, schema.nodes.bulletList, schema.nodes.orderedList];
10
+ return lists.some(function (list) {
11
+ return list === node.type;
12
+ });
13
+ };
14
+
15
+ /**
16
+ * Converts FROM taskList structure TO bulletList/orderedList structure.
17
+ */
18
+ var convertFromTaskListStructure = function convertFromTaskListStructure(node, targetListType, targetItemType) {
19
+ var schema = node.type.schema;
20
+ var targetListNodeType = schema.nodes[targetListType];
21
+ var convertedItems = [];
22
+ node.content.forEach(function (child) {
23
+ if (isListType(child, schema)) {
24
+ // This is a nested list - it should become a child of the previous item
25
+ if (convertedItems.length > 0) {
26
+ var previousItem = convertedItems[convertedItems.length - 1];
27
+ // Convert the nested list and add it to the previous item's content
28
+ var convertedNestedList = _transformList(child, targetListType, targetItemType);
29
+ var newContent = previousItem.content.append(_model.Fragment.from([convertedNestedList]));
30
+ var updatedItem = previousItem.type.create(previousItem.attrs, newContent);
31
+ convertedItems[convertedItems.length - 1] = updatedItem;
32
+ }
33
+ // If there's no previous item, skip this nested list (orphaned)
34
+ } else {
35
+ var convertedItem = transformListItem(child, targetItemType, targetListType);
36
+ if (convertedItem) {
37
+ convertedItems.push(convertedItem);
38
+ }
39
+ }
40
+ });
41
+ return targetListNodeType.create(node.attrs, _model.Fragment.from(convertedItems));
42
+ };
43
+
44
+ /**
45
+ * Converts FROM bulletList/orderedList structure TO taskList structure.
46
+ */
47
+ var convertToTaskListStructure = function convertToTaskListStructure(node, targetListType, targetItemType) {
48
+ var schema = node.type.schema;
49
+ var targetListNodeType = schema.nodes[targetListType];
50
+ var transformedContent = [];
51
+ node.content.forEach(function (itemNode) {
52
+ var transformedItem = transformListItem(itemNode, targetItemType, targetListType, true);
53
+ if (transformedItem) {
54
+ transformedContent.push(transformedItem);
55
+ }
56
+ itemNode.content.forEach(function (child) {
57
+ if (isListType(child, schema)) {
58
+ var transformedNestedList = _transformList(child, targetListType, targetItemType);
59
+ transformedContent.push(transformedNestedList);
60
+ }
61
+ });
62
+ });
63
+ return targetListNodeType.create(node.attrs, _model.Fragment.from(transformedContent));
64
+ };
65
+
66
+ /**
67
+ * Converts a single list item (listItem or taskItem) to the target item type.
68
+ * Handles content transformation based on the target type's requirements.
69
+ * @param itemNode - The list item node to convert
70
+ * @param targetItemType - The target item type (listItem or taskItem)
71
+ * @param targetListType - The target list type (bulletList, orderedList, or taskList)
72
+ * @param excludeNestedLists - When true, nested lists are excluded from the item's content
73
+ * (used when converting to taskList where nested lists become siblings)
74
+ */
75
+ var transformListItem = function transformListItem(itemNode, targetItemType, targetListType) {
76
+ var excludeNestedLists = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
77
+ var schema = itemNode.type.schema;
78
+ var targetItemNodeType = schema.nodes[targetItemType];
79
+ var isTargetTaskItem = targetItemType === 'taskItem';
80
+ var isSourceTaskItem = itemNode.type.name === 'taskItem';
81
+ var paragraphType = schema.nodes.paragraph;
82
+ if (!targetItemNodeType) {
83
+ return null;
84
+ }
85
+ if (isTargetTaskItem) {
86
+ var inlineContent = [];
87
+ itemNode.content.forEach(function (child) {
88
+ if (child.type === paragraphType) {
89
+ child.content.forEach(function (inline) {
90
+ inlineContent.push(inline);
91
+ });
92
+ }
93
+ if (child.isText) {
94
+ inlineContent.push(child);
95
+ }
96
+ // TODO: EDITOR-3887 - Skip mediaSingle, codeBlock, and nested lists
97
+ // Nested lists will be extracted and placed as siblings in the taskList
98
+ });
99
+ return targetItemNodeType.create({}, _model.Fragment.from(inlineContent));
100
+ } else {
101
+ var newContent = [];
102
+ if (isSourceTaskItem) {
103
+ newContent.push(paragraphType.create(null, itemNode.content));
104
+ } else {
105
+ itemNode.content.forEach(function (child) {
106
+ if (isListType(child, schema)) {
107
+ if (excludeNestedLists) {
108
+ // Skip nested lists - they will be handled separately as siblings
109
+ return;
110
+ }
111
+ newContent.push(_transformList(child, targetListType, targetItemType));
112
+ } else {
113
+ newContent.push(child);
114
+ }
115
+ });
116
+ }
117
+ if (newContent.length === 0) {
118
+ newContent.push(paragraphType.create());
119
+ }
120
+ return targetItemNodeType.create({}, _model.Fragment.from(newContent));
121
+ }
122
+ };
123
+
124
+ /**
125
+ * Recursively converts nested lists to the target list type.
126
+ * This function handles the conversion of both the list container and its items,
127
+ * including any nested lists within those items.
128
+ *
129
+ * Important: taskList has a different nesting structure than bulletList/orderedList:
130
+ * - taskList: nested taskLists are SIBLINGS of taskItems in the parent taskList
131
+ * - bulletList/orderedList: nested lists are CHILDREN of listItems
132
+ */
133
+ var _transformList = function transformList(node, targetListType, targetItemType) {
134
+ var schema = node.type.schema;
135
+ var targetListNodeType = schema.nodes[targetListType];
136
+ var targetItemNodeType = schema.nodes[targetItemType];
137
+ var taskListType = schema.nodes.taskList;
138
+ if (!targetListNodeType || !targetItemNodeType) {
139
+ return node;
140
+ }
141
+ var isSourceTaskList = node.type === taskListType;
142
+ var isTargetTaskList = targetListType === 'taskList';
143
+ if (isSourceTaskList && !isTargetTaskList) {
144
+ return convertFromTaskListStructure(node, targetListType, targetItemType);
145
+ } else if (!isSourceTaskList && isTargetTaskList) {
146
+ return convertToTaskListStructure(node, targetListType, targetItemType);
147
+ } else {
148
+ var transformedItems = [];
149
+ node.content.forEach(function (childNode) {
150
+ var transformedItem = isListType(childNode, schema) ? _transformList(childNode, targetListType, targetItemType) : transformListItem(childNode, targetItemType, targetListType);
151
+ if (transformedItem) {
152
+ transformedItems.push(transformedItem);
153
+ }
154
+ });
155
+ return targetListNodeType.create(node.attrs, _model.Fragment.from(transformedItems));
156
+ }
157
+ };
158
+
159
+ /**
160
+ * Transform step that converts between bulletList, orderedList, and taskList types.
161
+ * This step maintains the order and indentation of the list by recursively
162
+ * converting all nested lists while preserving the structure. It also handles
163
+ * conversion between listItem and taskItem types.
164
+ *
165
+ * When converting to taskList/taskItem, unsupported content (images, codeBlocks) is filtered out.
166
+ *
167
+ * @example
168
+ * Input (bulletList with nested bulletList):
169
+ * - bulletList
170
+ * - listItem "1"
171
+ * - bulletList
172
+ * - listItem "1.1"
173
+ * - bulletList
174
+ * - listItem "1.1.1"
175
+ * - listItem "1.2"
176
+ * - listItem "2"
177
+ *
178
+ * Output (orderedList with nested orderedList):
179
+ * 1. orderedList
180
+ * 1. listItem "1"
181
+ * 1. orderedList
182
+ * 1. listItem "1.1"
183
+ * 1. orderedList
184
+ * 1. listItem "1.1.1"
185
+ * 2. listItem "1.2"
186
+ * 2. listItem "2"
187
+ *
188
+ * @example
189
+ * Input (bulletList with nested taskList):
190
+ * - bulletList
191
+ * - listItem "Regular item"
192
+ * - taskList
193
+ * - taskItem "Task 1" (checked)
194
+ * - taskItem "Task 2" (unchecked)
195
+ *
196
+ * Output (orderedList with nested orderedList, taskItems converted to listItems):
197
+ * 1. orderedList
198
+ * 1. listItem "Regular item"
199
+ * 1. orderedList
200
+ * 1. listItem "Task 1"
201
+ * 2. listItem "Task 2"
202
+ *
203
+ * @example
204
+ * Input (bulletList to taskList, with paragraph extraction):
205
+ * - bulletList
206
+ * - listItem
207
+ * - paragraph "Text content"
208
+ * - listItem
209
+ * - paragraph "Text"
210
+ * - codeBlock "code"
211
+ * - mediaSingle (image)
212
+ *
213
+ * Output (taskList with text extracted from paragraphs, unsupported content filtered):
214
+ * - taskList
215
+ * - taskItem "Text content" (text extracted from paragraph)
216
+ * - taskItem "Text" (text extracted, codeBlock and image filtered out)
217
+ *
218
+ * @param nodes - The nodes to transform
219
+ * @param context - The transformation context containing schema and target node type
220
+ * @returns The transformed nodes
221
+ */
222
+ var listToListStep = exports.listToListStep = function listToListStep(nodes, context) {
223
+ var schema = context.schema,
224
+ targetNodeTypeName = context.targetNodeTypeName;
225
+ return nodes.map(function (node) {
226
+ if (isListType(node, schema)) {
227
+ var targetItemType = targetNodeTypeName === 'taskList' ? 'taskItem' : 'listItem';
228
+ return _transformList(node, targetNodeTypeName, targetItemType);
229
+ }
230
+ return node;
231
+ });
232
+ };
@@ -1,36 +1,33 @@
1
1
  "use strict";
2
2
 
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
3
  Object.defineProperty(exports, "__esModule", {
5
4
  value: true
6
5
  });
7
6
  exports.wrapMixedContentStep = void 0;
8
- var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
7
  var _model = require("@atlaskit/editor-prosemirror/model");
10
- var _types = require("./types");
11
- var _unwrapStep = require("./unwrapStep");
8
+ var _types = require("../types");
12
9
  /**
13
- * Determines if a node can be flattened (unwrapped and its contents merged).
14
- *
15
- * According to the text transformations list, flattenable nodes are:
16
- * - Bulleted list, Numbered list, Task list
17
- * - Text nodes (heading, paragraph)
18
- *
19
- * Containers (panels, expands, layouts, blockquotes) and atomic nodes (tables, media, macros) break out.
10
+ * Determines if a node is a text node (heading or paragraph).
11
+ * Text nodes can have their content converted to paragraphs when they can't be wrapped directly.
20
12
  */
21
- var canFlatten = function canFlatten(node) {
13
+ var isTextNode = function isTextNode(node) {
22
14
  var category = _types.NODE_CATEGORY_BY_TYPE[node.type.name];
23
- // Text and list nodes can be flattened (converted to simpler forms)
24
- return category === 'text' || category === 'list';
15
+ return category === 'text';
25
16
  };
26
17
 
27
18
  /**
28
- * Flattens a node by extracting its contents using the appropriate unwrap step.
29
- * This is only called for text and list nodes that can be converted to simpler forms.
30
- * Uses unwrapStep to extract children from list containers.
19
+ * Converts a text node (heading, paragraph) to a paragraph preserving its inline content.
20
+ * This is used when a text node can't be wrapped directly in the target container
21
+ * (e.g., heading can't go in blockquote, so it becomes a paragraph).
31
22
  */
32
- var flattenNode = function flattenNode(node, context) {
33
- return (0, _unwrapStep.unwrapStep)([node], context);
23
+ var convertTextNodeToParagraph = function convertTextNodeToParagraph(node, schema) {
24
+ var _schema$nodes$paragra;
25
+ // If it's already a paragraph, return as-is
26
+ if (node.type.name === 'paragraph') {
27
+ return node;
28
+ }
29
+ // Convert heading (or other text node) to paragraph with same inline content
30
+ return (_schema$nodes$paragra = schema.nodes.paragraph.createAndFill({}, node.content)) !== null && _schema$nodes$paragra !== void 0 ? _schema$nodes$paragra : null;
34
31
  };
35
32
 
36
33
  /**
@@ -123,13 +120,16 @@ var wrapMixedContentStep = exports.wrapMixedContentStep = function wrapMixedCont
123
120
  if (expandNode) {
124
121
  result.push(expandNode);
125
122
  }
126
- } else if (canFlatten(node)) {
127
- var _currentContainerCont;
128
- // Node cannot be wrapped but CAN be flattened - flatten and add to container
129
- var flattenedNodes = flattenNode(node, context);
130
- (_currentContainerCont = currentContainerContent).push.apply(_currentContainerCont, (0, _toConsumableArray2.default)(flattenedNodes));
123
+ } else if (isTextNode(node)) {
124
+ // Text node (heading, paragraph) that can't be wrapped - convert to paragraph
125
+ // Example: heading can't go in blockquote, so convert to paragraph with same content
126
+ var paragraph = convertTextNodeToParagraph(node, schema);
127
+ if (paragraph) {
128
+ currentContainerContent.push(paragraph);
129
+ }
131
130
  } else {
132
- // Node cannot be wrapped AND cannot be flattened (containers, tables, media, macros) - break out
131
+ // All other nodes that cannot be wrapped (lists, containers, tables, media, macros) - break out
132
+ // This includes list nodes like taskList that can't be placed in certain containers
133
133
  flushCurrentContainer();
134
134
  result.push(node);
135
135
  }
@@ -5,19 +5,17 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.getOutputNodes = void 0;
7
7
  var _utils = require("../transform-node-utils/utils");
8
- var _flattenListStep = require("./flattenListStep");
9
8
  var _flattenStep = require("./flattenStep");
10
- var _convertBulletListToTextStep = require("./steps/convertBulletListToTextStep");
11
- var _convertOrderedListToTextStep = require("./steps/convertOrderedListToTextStep");
12
- var _convertTaskListToTextStep = require("./steps/convertTaskListToTextStep");
9
+ var _flattenListStep = require("./steps/flattenListStep");
10
+ var _listToListStep = require("./steps/listToListStep");
13
11
  var _unwrapLayoutStep = require("./steps/unwrapLayoutStep");
12
+ var _unwrapListStep = require("./steps/unwrapListStep");
13
+ var _wrapMixedContentStep = require("./steps/wrapMixedContentStep");
14
14
  var _stubStep = require("./stubStep");
15
15
  var _types = require("./types");
16
16
  var _unwrapExpandStep = require("./unwrapExpandStep");
17
- var _unwrapListStep = require("./unwrapListStep");
18
17
  var _unwrapStep = require("./unwrapStep");
19
18
  var _wrapIntoLayoutStep = require("./wrapIntoLayoutStep");
20
- var _wrapMixedContentStep = require("./wrapMixedContentStep");
21
19
  var _wrapStep = require("./wrapStep");
22
20
  // Exampled step for overrides:
23
21
  // - open Block menu on a paragraph, click 'Panel' in the Turn into'
@@ -44,7 +42,7 @@ var TRANSFORM_STEPS = {
44
42
  list: {
45
43
  atomic: undefined,
46
44
  container: [_wrapStep.wrapStep],
47
- list: [_stubStep.stubStep],
45
+ list: [_listToListStep.listToListStep],
48
46
  text: [_flattenListStep.flattenListStep, _unwrapListStep.unwrapListStep]
49
47
  },
50
48
  text: {
@@ -63,7 +61,8 @@ var TRANSFORM_STEPS_OVERRIDE = {
63
61
  },
64
62
  panel: {
65
63
  layoutSection: [_unwrapStep.unwrapStep, _wrapIntoLayoutStep.wrapIntoLayoutStep],
66
- codeBlock: [_unwrapStep.unwrapStep, _flattenStep.flattenStep, _wrapStep.wrapStep]
64
+ codeBlock: [_unwrapStep.unwrapStep, _flattenStep.flattenStep, _wrapStep.wrapStep],
65
+ blockquote: [_unwrapStep.unwrapStep, _wrapMixedContentStep.wrapMixedContentStep]
67
66
  },
68
67
  expand: {
69
68
  panel: [_unwrapExpandStep.unwrapExpandStep, _wrapMixedContentStep.wrapMixedContentStep],
@@ -99,21 +98,19 @@ var TRANSFORM_STEPS_OVERRIDE = {
99
98
  panel: [_wrapStep.wrapStep]
100
99
  },
101
100
  bulletList: {
102
- // Warning: Actuall transformation logic not complete (Likelly prosemirror-markdown to be used)
103
- codeBlock: [_convertBulletListToTextStep.convertBulletListToTextStep, _flattenStep.flattenStep, _wrapStep.wrapStep],
101
+ // Text transformations currently not in scope > options will be disabled > stubbing in case
102
+ codeBlock: [_stubStep.stubStep],
104
103
  layoutSection: [_wrapIntoLayoutStep.wrapIntoLayoutStep]
105
104
  },
106
105
  orderedList: {
107
- // Warning: Actuall transformation logic not complete (Likelly prosemirror-markdown to be used)
108
- codeBlock: [_convertOrderedListToTextStep.convertOrderedListToTextStep, _flattenStep.flattenStep, _wrapStep.wrapStep],
109
- // Warning: Actuall transformation logic not complete (Likelly prosemirror-markdown to be used)
106
+ // Text transformations currently not in scope > options will be disabled > stubbing in case
107
+ codeBlock: [_stubStep.stubStep],
110
108
  layoutSection: [_wrapIntoLayoutStep.wrapIntoLayoutStep]
111
109
  },
112
110
  taskList: {
113
- // Warning: Actuall transformation logic not complete (Skeptical that prosemirror-markdown can be used)
114
- blockquote: [_convertTaskListToTextStep.convertTaskListToTextStep, _wrapStep.wrapStep],
115
- // Warning: Actuall transformation logic not complete (Likelly prosemirror-markdown to be used)
116
- codeBlock: [_convertTaskListToTextStep.convertTaskListToTextStep, _flattenStep.flattenStep, _wrapStep.wrapStep],
111
+ // Text transformations currently not in scope > options will be disabled > stubbing in case
112
+ blockquote: [_stubStep.stubStep],
113
+ codeBlock: [_stubStep.stubStep],
117
114
  layoutSection: [_wrapIntoLayoutStep.wrapIntoLayoutStep]
118
115
  },
119
116
  table: {
@@ -10,12 +10,10 @@ const extractNestedLists = (node, listTypes, itemTypes, schema) => {
10
10
  child.forEach(grandChild => {
11
11
  if (listTypes.some(type => grandChild.type === type)) {
12
12
  nestedLists.push(grandChild);
13
+ } else if (grandChild.isText) {
14
+ contentWithoutNestedLists.push(paragraph.createAndFill({}, grandChild));
13
15
  } else {
14
- if (grandChild.isText) {
15
- contentWithoutNestedLists.push(paragraph.createAndFill({}, grandChild));
16
- } else {
17
- contentWithoutNestedLists.push(grandChild);
18
- }
16
+ contentWithoutNestedLists.push(grandChild);
19
17
  }
20
18
  });
21
19
  items.push(child.copy(Fragment.from(contentWithoutNestedLists)));
@@ -0,0 +1,225 @@
1
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
+ const isListType = (node, schema) => {
3
+ const lists = [schema.nodes.taskList, schema.nodes.bulletList, schema.nodes.orderedList];
4
+ return lists.some(list => list === node.type);
5
+ };
6
+
7
+ /**
8
+ * Converts FROM taskList structure TO bulletList/orderedList structure.
9
+ */
10
+ const convertFromTaskListStructure = (node, targetListType, targetItemType) => {
11
+ const schema = node.type.schema;
12
+ const targetListNodeType = schema.nodes[targetListType];
13
+ const convertedItems = [];
14
+ node.content.forEach(child => {
15
+ if (isListType(child, schema)) {
16
+ // This is a nested list - it should become a child of the previous item
17
+ if (convertedItems.length > 0) {
18
+ const previousItem = convertedItems[convertedItems.length - 1];
19
+ // Convert the nested list and add it to the previous item's content
20
+ const convertedNestedList = transformList(child, targetListType, targetItemType);
21
+ const newContent = previousItem.content.append(Fragment.from([convertedNestedList]));
22
+ const updatedItem = previousItem.type.create(previousItem.attrs, newContent);
23
+ convertedItems[convertedItems.length - 1] = updatedItem;
24
+ }
25
+ // If there's no previous item, skip this nested list (orphaned)
26
+ } else {
27
+ const convertedItem = transformListItem(child, targetItemType, targetListType);
28
+ if (convertedItem) {
29
+ convertedItems.push(convertedItem);
30
+ }
31
+ }
32
+ });
33
+ return targetListNodeType.create(node.attrs, Fragment.from(convertedItems));
34
+ };
35
+
36
+ /**
37
+ * Converts FROM bulletList/orderedList structure TO taskList structure.
38
+ */
39
+ const convertToTaskListStructure = (node, targetListType, targetItemType) => {
40
+ const schema = node.type.schema;
41
+ const targetListNodeType = schema.nodes[targetListType];
42
+ const transformedContent = [];
43
+ node.content.forEach(itemNode => {
44
+ const transformedItem = transformListItem(itemNode, targetItemType, targetListType, true);
45
+ if (transformedItem) {
46
+ transformedContent.push(transformedItem);
47
+ }
48
+ itemNode.content.forEach(child => {
49
+ if (isListType(child, schema)) {
50
+ const transformedNestedList = transformList(child, targetListType, targetItemType);
51
+ transformedContent.push(transformedNestedList);
52
+ }
53
+ });
54
+ });
55
+ return targetListNodeType.create(node.attrs, Fragment.from(transformedContent));
56
+ };
57
+
58
+ /**
59
+ * Converts a single list item (listItem or taskItem) to the target item type.
60
+ * Handles content transformation based on the target type's requirements.
61
+ * @param itemNode - The list item node to convert
62
+ * @param targetItemType - The target item type (listItem or taskItem)
63
+ * @param targetListType - The target list type (bulletList, orderedList, or taskList)
64
+ * @param excludeNestedLists - When true, nested lists are excluded from the item's content
65
+ * (used when converting to taskList where nested lists become siblings)
66
+ */
67
+ const transformListItem = (itemNode, targetItemType, targetListType, excludeNestedLists = false) => {
68
+ const schema = itemNode.type.schema;
69
+ const targetItemNodeType = schema.nodes[targetItemType];
70
+ const isTargetTaskItem = targetItemType === 'taskItem';
71
+ const isSourceTaskItem = itemNode.type.name === 'taskItem';
72
+ const paragraphType = schema.nodes.paragraph;
73
+ if (!targetItemNodeType) {
74
+ return null;
75
+ }
76
+ if (isTargetTaskItem) {
77
+ const inlineContent = [];
78
+ itemNode.content.forEach(child => {
79
+ if (child.type === paragraphType) {
80
+ child.content.forEach(inline => {
81
+ inlineContent.push(inline);
82
+ });
83
+ }
84
+ if (child.isText) {
85
+ inlineContent.push(child);
86
+ }
87
+ // TODO: EDITOR-3887 - Skip mediaSingle, codeBlock, and nested lists
88
+ // Nested lists will be extracted and placed as siblings in the taskList
89
+ });
90
+ return targetItemNodeType.create({}, Fragment.from(inlineContent));
91
+ } else {
92
+ const newContent = [];
93
+ if (isSourceTaskItem) {
94
+ newContent.push(paragraphType.create(null, itemNode.content));
95
+ } else {
96
+ itemNode.content.forEach(child => {
97
+ if (isListType(child, schema)) {
98
+ if (excludeNestedLists) {
99
+ // Skip nested lists - they will be handled separately as siblings
100
+ return;
101
+ }
102
+ newContent.push(transformList(child, targetListType, targetItemType));
103
+ } else {
104
+ newContent.push(child);
105
+ }
106
+ });
107
+ }
108
+ if (newContent.length === 0) {
109
+ newContent.push(paragraphType.create());
110
+ }
111
+ return targetItemNodeType.create({}, Fragment.from(newContent));
112
+ }
113
+ };
114
+
115
+ /**
116
+ * Recursively converts nested lists to the target list type.
117
+ * This function handles the conversion of both the list container and its items,
118
+ * including any nested lists within those items.
119
+ *
120
+ * Important: taskList has a different nesting structure than bulletList/orderedList:
121
+ * - taskList: nested taskLists are SIBLINGS of taskItems in the parent taskList
122
+ * - bulletList/orderedList: nested lists are CHILDREN of listItems
123
+ */
124
+ const transformList = (node, targetListType, targetItemType) => {
125
+ const schema = node.type.schema;
126
+ const targetListNodeType = schema.nodes[targetListType];
127
+ const targetItemNodeType = schema.nodes[targetItemType];
128
+ const taskListType = schema.nodes.taskList;
129
+ if (!targetListNodeType || !targetItemNodeType) {
130
+ return node;
131
+ }
132
+ const isSourceTaskList = node.type === taskListType;
133
+ const isTargetTaskList = targetListType === 'taskList';
134
+ if (isSourceTaskList && !isTargetTaskList) {
135
+ return convertFromTaskListStructure(node, targetListType, targetItemType);
136
+ } else if (!isSourceTaskList && isTargetTaskList) {
137
+ return convertToTaskListStructure(node, targetListType, targetItemType);
138
+ } else {
139
+ const transformedItems = [];
140
+ node.content.forEach(childNode => {
141
+ const transformedItem = isListType(childNode, schema) ? transformList(childNode, targetListType, targetItemType) : transformListItem(childNode, targetItemType, targetListType);
142
+ if (transformedItem) {
143
+ transformedItems.push(transformedItem);
144
+ }
145
+ });
146
+ return targetListNodeType.create(node.attrs, Fragment.from(transformedItems));
147
+ }
148
+ };
149
+
150
+ /**
151
+ * Transform step that converts between bulletList, orderedList, and taskList types.
152
+ * This step maintains the order and indentation of the list by recursively
153
+ * converting all nested lists while preserving the structure. It also handles
154
+ * conversion between listItem and taskItem types.
155
+ *
156
+ * When converting to taskList/taskItem, unsupported content (images, codeBlocks) is filtered out.
157
+ *
158
+ * @example
159
+ * Input (bulletList with nested bulletList):
160
+ * - bulletList
161
+ * - listItem "1"
162
+ * - bulletList
163
+ * - listItem "1.1"
164
+ * - bulletList
165
+ * - listItem "1.1.1"
166
+ * - listItem "1.2"
167
+ * - listItem "2"
168
+ *
169
+ * Output (orderedList with nested orderedList):
170
+ * 1. orderedList
171
+ * 1. listItem "1"
172
+ * 1. orderedList
173
+ * 1. listItem "1.1"
174
+ * 1. orderedList
175
+ * 1. listItem "1.1.1"
176
+ * 2. listItem "1.2"
177
+ * 2. listItem "2"
178
+ *
179
+ * @example
180
+ * Input (bulletList with nested taskList):
181
+ * - bulletList
182
+ * - listItem "Regular item"
183
+ * - taskList
184
+ * - taskItem "Task 1" (checked)
185
+ * - taskItem "Task 2" (unchecked)
186
+ *
187
+ * Output (orderedList with nested orderedList, taskItems converted to listItems):
188
+ * 1. orderedList
189
+ * 1. listItem "Regular item"
190
+ * 1. orderedList
191
+ * 1. listItem "Task 1"
192
+ * 2. listItem "Task 2"
193
+ *
194
+ * @example
195
+ * Input (bulletList to taskList, with paragraph extraction):
196
+ * - bulletList
197
+ * - listItem
198
+ * - paragraph "Text content"
199
+ * - listItem
200
+ * - paragraph "Text"
201
+ * - codeBlock "code"
202
+ * - mediaSingle (image)
203
+ *
204
+ * Output (taskList with text extracted from paragraphs, unsupported content filtered):
205
+ * - taskList
206
+ * - taskItem "Text content" (text extracted from paragraph)
207
+ * - taskItem "Text" (text extracted, codeBlock and image filtered out)
208
+ *
209
+ * @param nodes - The nodes to transform
210
+ * @param context - The transformation context containing schema and target node type
211
+ * @returns The transformed nodes
212
+ */
213
+ export const listToListStep = (nodes, context) => {
214
+ const {
215
+ schema,
216
+ targetNodeTypeName
217
+ } = context;
218
+ return nodes.map(node => {
219
+ if (isListType(node, schema)) {
220
+ const targetItemType = targetNodeTypeName === 'taskList' ? 'taskItem' : 'listItem';
221
+ return transformList(node, targetNodeTypeName, targetItemType);
222
+ }
223
+ return node;
224
+ });
225
+ };