@atlaskit/editor-plugin-block-menu 5.2.7 → 5.2.9

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 (25) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/editor-actions/index.js +3 -13
  3. package/dist/cjs/editor-commands/transform-node-utils/steps/listToDecisionListStep.js +12 -12
  4. package/dist/cjs/editor-commands/transform-node-utils/steps/listToListStep.js +96 -119
  5. package/dist/cjs/editor-commands/transform-node-utils/transform.js +8 -2
  6. package/dist/cjs/editor-commands/transform-node-utils/utils.js +17 -1
  7. package/dist/cjs/editor-commands/transform-node-utils/wrapIntoListStep.js +17 -0
  8. package/dist/cjs/editor-commands/transformNode.js +13 -4
  9. package/dist/es2019/editor-actions/index.js +1 -11
  10. package/dist/es2019/editor-commands/transform-node-utils/steps/listToDecisionListStep.js +10 -12
  11. package/dist/es2019/editor-commands/transform-node-utils/steps/listToListStep.js +93 -118
  12. package/dist/es2019/editor-commands/transform-node-utils/transform.js +8 -2
  13. package/dist/es2019/editor-commands/transform-node-utils/utils.js +17 -1
  14. package/dist/es2019/editor-commands/transform-node-utils/wrapIntoListStep.js +13 -0
  15. package/dist/es2019/editor-commands/transformNode.js +15 -4
  16. package/dist/esm/editor-actions/index.js +2 -13
  17. package/dist/esm/editor-commands/transform-node-utils/steps/listToDecisionListStep.js +11 -12
  18. package/dist/esm/editor-commands/transform-node-utils/steps/listToListStep.js +95 -119
  19. package/dist/esm/editor-commands/transform-node-utils/transform.js +8 -2
  20. package/dist/esm/editor-commands/transform-node-utils/utils.js +17 -1
  21. package/dist/esm/editor-commands/transform-node-utils/wrapIntoListStep.js +11 -0
  22. package/dist/esm/editor-commands/transformNode.js +13 -4
  23. package/dist/types/editor-commands/transform-node-utils/wrapIntoListStep.d.ts +3 -0
  24. package/dist/types-ts4.5/editor-commands/transform-node-utils/wrapIntoListStep.d.ts +3 -0
  25. package/package.json +5 -5
@@ -2,146 +2,119 @@ import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
2
  import { isListType } from '../utils';
3
3
 
4
4
  /**
5
- * Converts FROM taskList structure TO bulletList/orderedList structure.
6
- */
7
- const convertFromTaskListStructure = (node, targetListType, targetItemType) => {
8
- const schema = node.type.schema;
9
- const targetListNodeType = schema.nodes[targetListType];
10
- const convertedItems = [];
11
- node.content.forEach(child => {
12
- if (isListType(child, schema)) {
13
- // This is a nested list - it should become a child of the previous item
14
- if (convertedItems.length > 0) {
15
- const previousItem = convertedItems[convertedItems.length - 1];
16
- // Convert the nested list and add it to the previous item's content
17
- const convertedNestedList = transformList(child, targetListType, targetItemType);
18
- const newContent = previousItem.content.append(Fragment.from([convertedNestedList]));
19
- const updatedItem = previousItem.type.create(previousItem.attrs, newContent);
20
- convertedItems[convertedItems.length - 1] = updatedItem;
21
- }
22
- // If there's no previous item, skip this nested list (orphaned)
23
- } else {
24
- const convertedItem = transformListItem(child, targetItemType, targetListType);
25
- if (convertedItem) {
26
- convertedItems.push(convertedItem);
27
- }
28
- }
29
- });
30
- return targetListNodeType.create(node.attrs, Fragment.from(convertedItems));
31
- };
32
-
33
- /**
34
- * Converts FROM bulletList/orderedList structure TO taskList structure.
5
+ * Recursively converts nested lists to the target list type.
6
+ * This function handles the conversion of both the list container and its items,
7
+ * including any nested lists within those items.
8
+ *
9
+ * Important: taskList has a different nesting structure than bulletList/orderedList:
10
+ * - taskList: nested taskLists are SIBLINGS of taskItems in the parent taskList
11
+ * - bulletList/orderedList: nested lists are CHILDREN of listItems
35
12
  */
36
- const convertToTaskListStructure = (node, targetListType, targetItemType) => {
13
+ const transformList = (node, targetListType, targetItemType, unsupportedContent) => {
37
14
  const schema = node.type.schema;
38
- const targetListNodeType = schema.nodes[targetListType];
39
- const transformedContent = [];
40
- node.content.forEach(itemNode => {
41
- const transformedItem = transformListItem(itemNode, targetItemType, targetListType, true);
42
- if (transformedItem) {
43
- transformedContent.push(transformedItem);
44
- }
45
- itemNode.content.forEach(child => {
15
+ const taskListType = schema.nodes.taskList;
16
+ const isSourceTaskList = node.type === taskListType;
17
+ const isTargetTaskList = targetListType === 'taskList';
18
+ const convertFromTaskListStructure = (node, targetListType, targetItemType) => {
19
+ const schema = node.type.schema;
20
+ const targetListNodeType = schema.nodes[targetListType];
21
+ const transformedContent = [];
22
+ node.forEach(child => {
46
23
  if (isListType(child, schema)) {
47
- const transformedNestedList = transformList(child, targetListType, targetItemType);
48
- transformedContent.push(transformedNestedList);
24
+ // This is a nested list - it should become a child of the previous item
25
+ if (transformedContent.length > 0) {
26
+ const previousItem = transformedContent[transformedContent.length - 1];
27
+ // Convert the nested list and add it to the previous item's content
28
+ const transformedNestedList = transformList(child, targetListType, targetItemType, unsupportedContent);
29
+ const newContent = previousItem.content.append(Fragment.from([transformedNestedList]));
30
+ const updatedItem = previousItem.type.create(previousItem.attrs, newContent);
31
+ transformedContent[transformedContent.length - 1] = updatedItem;
32
+ }
33
+ // If there's no previous item, skip this nested list (orphaned)
34
+ } else {
35
+ const transformedItem = transformListItem(child, targetItemType, targetListType);
36
+ if (transformedItem) {
37
+ transformedContent.push(transformedItem);
38
+ }
49
39
  }
50
40
  });
51
- });
52
- return targetListNodeType.create(node.attrs, Fragment.from(transformedContent));
53
- };
54
-
55
- /**
56
- * Converts a single list item (listItem or taskItem) to the target item type.
57
- * Handles content transformation based on the target type's requirements.
58
- * @param itemNode - The list item node to convert
59
- * @param targetItemType - The target item type (listItem or taskItem)
60
- * @param targetListType - The target list type (bulletList, orderedList, or taskList)
61
- * @param excludeNestedLists - When true, nested lists are excluded from the item's content
62
- * (used when converting to taskList where nested lists become siblings)
63
- */
64
- const transformListItem = (itemNode, targetItemType, targetListType, excludeNestedLists = false) => {
65
- const schema = itemNode.type.schema;
66
- const targetItemNodeType = schema.nodes[targetItemType];
67
- const isTargetTaskItem = targetItemType === 'taskItem';
68
- const isSourceTaskItem = itemNode.type.name === 'taskItem';
69
- const paragraphType = schema.nodes.paragraph;
70
- if (!targetItemNodeType) {
71
- return null;
72
- }
73
- if (isTargetTaskItem) {
74
- const inlineContent = [];
75
- itemNode.content.forEach(child => {
76
- if (child.type === paragraphType) {
77
- child.content.forEach(inline => {
78
- inlineContent.push(inline);
79
- });
80
- }
81
- if (child.isText) {
82
- inlineContent.push(child);
41
+ return targetListNodeType.create(node.attrs, transformedContent);
42
+ };
43
+ const convertToTaskListStructure = (node, targetListType, targetItemType) => {
44
+ const schema = node.type.schema;
45
+ const targetListNodeType = schema.nodes[targetListType];
46
+ const transformedContent = [];
47
+ node.forEach(itemNode => {
48
+ const transformedItem = transformListItem(itemNode, targetItemType, targetListType, true);
49
+ if (transformedItem) {
50
+ transformedContent.push(transformedItem);
83
51
  }
84
- // TODO: EDITOR-3887 - Skip mediaSingle, codeBlock, and nested lists
85
- // Nested lists will be extracted and placed as siblings in the taskList
52
+ itemNode.forEach(child => {
53
+ if (isListType(child, schema)) {
54
+ transformedContent.push(transformList(child, targetListType, targetItemType, unsupportedContent));
55
+ }
56
+ });
86
57
  });
87
- return targetItemNodeType.create({}, Fragment.from(inlineContent));
88
- } else {
89
- const newContent = [];
58
+ return targetListNodeType.create(node.attrs, transformedContent);
59
+ };
60
+ const transformListItem = (itemNode, targetItemType, targetListType, excludeNestedLists = false) => {
61
+ const schema = itemNode.type.schema;
62
+ const targetItemNodeType = schema.nodes[targetItemType];
63
+ const isTargetTaskItem = targetItemType === 'taskItem';
64
+ const isSourceTaskItem = itemNode.type.name === 'taskItem';
65
+ const paragraphType = schema.nodes.paragraph;
66
+ if (isTargetTaskItem) {
67
+ const inlineContent = [];
68
+ itemNode.forEach(child => {
69
+ if (child.type === paragraphType) {
70
+ inlineContent.push(...child.children);
71
+ } else if (child.isText) {
72
+ inlineContent.push(child);
73
+ // Nested lists will be extracted and placed as siblings in the taskList
74
+ } else if (!isListType(child, schema)) {
75
+ unsupportedContent.push(child);
76
+ }
77
+ });
78
+ return targetItemNodeType.create({}, inlineContent);
79
+ }
80
+ const transformedContent = [];
90
81
  if (isSourceTaskItem) {
91
- newContent.push(paragraphType.create(null, itemNode.content));
82
+ transformedContent.push(paragraphType.create(null, itemNode.content));
92
83
  } else {
93
- itemNode.content.forEach(child => {
84
+ itemNode.forEach(child => {
94
85
  if (isListType(child, schema)) {
95
86
  if (excludeNestedLists) {
96
87
  // Skip nested lists - they will be handled separately as siblings
97
88
  return;
98
89
  }
99
- newContent.push(transformList(child, targetListType, targetItemType));
90
+ transformedContent.push(transformList(child, targetListType, targetItemType, unsupportedContent));
100
91
  } else {
101
- newContent.push(child);
92
+ transformedContent.push(child);
102
93
  }
103
94
  });
104
95
  }
105
- if (newContent.length === 0) {
106
- newContent.push(paragraphType.create());
96
+ if (transformedContent.length === 0) {
97
+ transformedContent.push(paragraphType.create());
107
98
  }
108
- return targetItemNodeType.create({}, Fragment.from(newContent));
109
- }
110
- };
111
-
112
- /**
113
- * Recursively converts nested lists to the target list type.
114
- * This function handles the conversion of both the list container and its items,
115
- * including any nested lists within those items.
116
- *
117
- * Important: taskList has a different nesting structure than bulletList/orderedList:
118
- * - taskList: nested taskLists are SIBLINGS of taskItems in the parent taskList
119
- * - bulletList/orderedList: nested lists are CHILDREN of listItems
120
- */
121
- const transformList = (node, targetListType, targetItemType) => {
122
- const schema = node.type.schema;
123
- const targetListNodeType = schema.nodes[targetListType];
124
- const targetItemNodeType = schema.nodes[targetItemType];
125
- const taskListType = schema.nodes.taskList;
126
- if (!targetListNodeType || !targetItemNodeType) {
127
- return node;
128
- }
129
- const isSourceTaskList = node.type === taskListType;
130
- const isTargetTaskList = targetListType === 'taskList';
99
+ return targetItemNodeType.create({}, transformedContent);
100
+ };
101
+ const convertList = (node, schema, targetListType, targetItemType) => {
102
+ const targetListNodeType = schema.nodes[targetListType];
103
+ const transformedContent = [];
104
+ node.forEach(childNode => {
105
+ const transformedItem = isListType(childNode, schema) ? transformList(childNode, targetListType, targetItemType, unsupportedContent) : transformListItem(childNode, targetItemType, targetListType);
106
+ if (transformedItem) {
107
+ transformedContent.push(transformedItem);
108
+ }
109
+ });
110
+ return targetListNodeType.create(node.attrs, transformedContent);
111
+ };
131
112
  if (isSourceTaskList && !isTargetTaskList) {
132
113
  return convertFromTaskListStructure(node, targetListType, targetItemType);
133
114
  } else if (!isSourceTaskList && isTargetTaskList) {
134
115
  return convertToTaskListStructure(node, targetListType, targetItemType);
135
- } else {
136
- const transformedItems = [];
137
- node.content.forEach(childNode => {
138
- const transformedItem = isListType(childNode, schema) ? transformList(childNode, targetListType, targetItemType) : transformListItem(childNode, targetItemType, targetListType);
139
- if (transformedItem) {
140
- transformedItems.push(transformedItem);
141
- }
142
- });
143
- return targetListNodeType.create(node.attrs, Fragment.from(transformedItems));
144
116
  }
117
+ return convertList(node, schema, targetListType, targetItemType);
145
118
  };
146
119
 
147
120
  /**
@@ -212,11 +185,13 @@ export const listToListStep = (nodes, context) => {
212
185
  schema,
213
186
  targetNodeTypeName
214
187
  } = context;
215
- return nodes.map(node => {
188
+ const unsupportedContent = [];
189
+ const transformedNodes = nodes.map(node => {
216
190
  if (isListType(node, schema)) {
217
191
  const targetItemType = targetNodeTypeName === 'taskList' ? 'taskItem' : 'listItem';
218
- return transformList(node, targetNodeTypeName, targetItemType);
192
+ return transformList(node, targetNodeTypeName, targetItemType, unsupportedContent);
219
193
  }
220
194
  return node;
221
195
  });
196
+ return [...transformedNodes, ...unsupportedContent];
222
197
  };
@@ -13,6 +13,7 @@ import { NODE_CATEGORY_BY_TYPE, toNodeTypeValue } from './types';
13
13
  import { unwrapExpandStep } from './unwrapExpandStep';
14
14
  import { unwrapStep } from './unwrapStep';
15
15
  import { wrapIntoLayoutStep } from './wrapIntoLayoutStep';
16
+ import { wrapIntoListStep } from './wrapIntoListStep';
16
17
  import { wrapStep } from './wrapStep';
17
18
 
18
19
  // Exampled step for overrides:
@@ -28,7 +29,7 @@ const TRANSFORM_STEPS = {
28
29
  atomic: {
29
30
  atomic: undefined,
30
31
  container: [wrapStep],
31
- list: undefined,
32
+ list: [wrapIntoListStep],
32
33
  text: undefined
33
34
  },
34
35
  container: {
@@ -116,7 +117,12 @@ const TRANSFORM_STEPS_OVERRIDE = {
116
117
  decisionList: [flattenListStep, listToDecisionListStep]
117
118
  },
118
119
  table: {
119
- expand: [wrapStep],
120
+ layoutSection: [wrapIntoLayoutStep]
121
+ },
122
+ mediaSingle: {
123
+ layoutSection: [wrapIntoLayoutStep]
124
+ },
125
+ mediaGroup: {
120
126
  layoutSection: [wrapIntoLayoutStep]
121
127
  },
122
128
  decisionList: {
@@ -73,13 +73,29 @@ export const expandSelectionToBlockRange = (selection, schema) => {
73
73
  const table = findTable(selection);
74
74
  if (table) {
75
75
  const $from = selection.$from.doc.resolve(table.pos);
76
- const $to = selection.$from.doc.resolve(table.pos + table.node.nodeSize - 1);
76
+ const $to = selection.$from.doc.resolve(table.pos + table.node.nodeSize);
77
77
  return {
78
78
  $from,
79
79
  $to
80
80
  };
81
81
  }
82
82
  }
83
+
84
+ // when selecting a file, selection is on media
85
+ // need to find media group and return its pos
86
+ if (selection instanceof NodeSelection) {
87
+ if (selection.node.type === nodes.media) {
88
+ const mediaGroup = findParentNodeOfType(nodes.mediaGroup)(selection);
89
+ if (mediaGroup) {
90
+ const $from = selection.$from.doc.resolve(mediaGroup.pos);
91
+ const $to = selection.$from.doc.resolve(mediaGroup.pos + mediaGroup.node.nodeSize);
92
+ return {
93
+ $from,
94
+ $to
95
+ };
96
+ }
97
+ }
98
+ }
83
99
  return expandToBlockRange(selection.$from, selection.$to, node => {
84
100
  if (nodesNeedToExpandRange.includes(node.type)) {
85
101
  return false;
@@ -0,0 +1,13 @@
1
+ /** wrap nodes into bullet list or numbered list, does not work for task list */
2
+ export const wrapIntoListStep = (nodes, context) => {
3
+ const {
4
+ schema,
5
+ targetNodeTypeName
6
+ } = context;
7
+ const listItemNode = schema.nodes.listItem.createAndFill({}, nodes);
8
+ const outputNode = schema.nodes[targetNodeTypeName].createAndFill({}, listItemNode);
9
+ if (outputNode) {
10
+ return [outputNode];
11
+ }
12
+ return nodes;
13
+ };
@@ -1,4 +1,5 @@
1
1
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
+ import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
2
3
  import { isNestedNode } from '../ui/utils/isNestedNode';
3
4
  import { getOutputNodes } from './transform-node-utils/transform';
4
5
  import { expandSelectionToBlockRange } from './transform-node-utils/utils';
@@ -14,10 +15,14 @@ export const transformNode = api =>
14
15
  if (!preservedSelection) {
15
16
  return tr;
16
17
  }
18
+ const schema = tr.doc.type.schema;
19
+ const {
20
+ nodes
21
+ } = schema;
17
22
  const {
18
23
  $from,
19
24
  $to
20
- } = expandSelectionToBlockRange(preservedSelection, tr.doc.type.schema);
25
+ } = expandSelectionToBlockRange(preservedSelection, schema);
21
26
  const isNested = isNestedNode(preservedSelection, '');
22
27
  const selectedParent = $from.parent;
23
28
  let fragment = Fragment.empty;
@@ -34,9 +39,15 @@ export const transformNode = api =>
34
39
  fragment = fragment.append(Fragment.fromArray(outputNode));
35
40
  }
36
41
  });
37
-
38
- // TODO: ED-12345 - selection is broken post transaction, to fix.
39
- tr.replaceWith(isList ? $from.pos - 1 : $from.pos, $to.pos, fragment);
42
+ const nodesToDeleteAndInsert = [nodes.mediaSingle];
43
+ if (preservedSelection instanceof NodeSelection && nodesToDeleteAndInsert.includes(preservedSelection.node.type)) {
44
+ // when node is media single, use tr.replaceWith freeze editor, if modify position, tr.replaceWith creates duplicats
45
+ tr.deleteRange($from.pos, $to.pos);
46
+ tr.insert($from.pos, fragment);
47
+ } else {
48
+ // TODO: ED-12345 - selection is broken post transaction, to fix.
49
+ tr.replaceWith(isList ? $from.pos - 1 : $from.pos, $to.pos, fragment);
50
+ }
40
51
  return tr;
41
52
  };
42
53
  };
@@ -1,3 +1,4 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
1
2
  /**
2
3
  * Create a simple registry for block menu components.
3
4
  *
@@ -48,19 +49,7 @@
48
49
  export var createBlockMenuRegistry = function createBlockMenuRegistry() {
49
50
  var components = [];
50
51
  var register = function register(blockMenuComponents) {
51
- blockMenuComponents.forEach(function (newComponent) {
52
- // Find if a component with the same key already exists
53
- var existingIndex = components.findIndex(function (comp) {
54
- return comp.key === newComponent.key;
55
- });
56
- if (existingIndex !== -1) {
57
- // Replace the existing component
58
- components[existingIndex] = newComponent;
59
- } else {
60
- // Add new component
61
- components.push(newComponent);
62
- }
63
- });
52
+ components.push.apply(components, _toConsumableArray(blockMenuComponents));
64
53
  };
65
54
  return {
66
55
  register: register,
@@ -1,4 +1,4 @@
1
- import { Fragment } from '@atlaskit/editor-prosemirror/model';
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
2
  import { isListType } from '../utils';
3
3
 
4
4
  /**
@@ -27,7 +27,8 @@ import { isListType } from '../utils';
27
27
  export var listToDecisionListStep = function listToDecisionListStep(nodes, context) {
28
28
  var schema = context.schema;
29
29
  var paragraphType = schema.nodes.paragraph;
30
- return nodes.map(function (node) {
30
+ var unsupportedContent = [];
31
+ var transformedNodes = nodes.map(function (node) {
31
32
  if (!isListType(node, schema)) {
32
33
  return node;
33
34
  }
@@ -37,20 +38,18 @@ export var listToDecisionListStep = function listToDecisionListStep(nodes, conte
37
38
  item.forEach(function (child) {
38
39
  if (child.type === paragraphType) {
39
40
  // paragraph may contain hard breaks etc.
40
- child.content.forEach(function (inline) {
41
- itemContent.push(inline);
42
- });
43
- } else {
41
+ itemContent.push.apply(itemContent, _toConsumableArray(child.children));
42
+ } else if (child.isText) {
44
43
  itemContent.push(child);
44
+ } else if (!isListType(child, schema)) {
45
+ unsupportedContent.push(child);
45
46
  }
46
- // TODO: EDITOR-3887 - Skip mediaSingle, codeBlock, and nested lists
47
47
  });
48
- var decisionItem = schema.nodes.decisionItem.create({}, Fragment.from(itemContent));
49
- if (decisionItem) {
50
- decisionItems.push(decisionItem);
51
- }
48
+ var decisionItem = schema.nodes.decisionItem.create({}, itemContent);
49
+ decisionItems.push(decisionItem);
52
50
  });
53
- var decisionList = schema.nodes.decisionList.create({}, Fragment.from(decisionItems));
51
+ var decisionList = schema.nodes.decisionList.create({}, decisionItems);
54
52
  return decisionList || node;
55
53
  });
54
+ return [].concat(_toConsumableArray(transformedNodes), unsupportedContent);
56
55
  };