@atlaskit/editor-plugin-block-menu 6.0.5 → 6.0.6

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 (23) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/cjs/editor-commands/transform-node-utils/steps/wrapBlockquoteToDecisionListStep.js +2 -12
  3. package/dist/cjs/editor-commands/transform-node-utils/steps/wrapMixedContentStep.js +69 -52
  4. package/dist/cjs/editor-commands/transform-node-utils/transform.js +18 -21
  5. package/dist/cjs/editor-commands/transform-node-utils/utils.js +10 -1
  6. package/dist/es2019/editor-commands/transform-node-utils/steps/wrapBlockquoteToDecisionListStep.js +1 -11
  7. package/dist/es2019/editor-commands/transform-node-utils/steps/wrapMixedContentStep.js +71 -53
  8. package/dist/es2019/editor-commands/transform-node-utils/transform.js +18 -21
  9. package/dist/es2019/editor-commands/transform-node-utils/utils.js +10 -0
  10. package/dist/esm/editor-commands/transform-node-utils/steps/wrapBlockquoteToDecisionListStep.js +1 -11
  11. package/dist/esm/editor-commands/transform-node-utils/steps/wrapMixedContentStep.js +70 -53
  12. package/dist/esm/editor-commands/transform-node-utils/transform.js +18 -21
  13. package/dist/esm/editor-commands/transform-node-utils/utils.js +10 -0
  14. package/dist/types/editor-commands/transform-node-utils/steps/wrapMixedContentStep.d.ts +17 -2
  15. package/dist/types/editor-commands/transform-node-utils/utils.d.ts +5 -0
  16. package/dist/types-ts4.5/editor-commands/transform-node-utils/steps/wrapMixedContentStep.d.ts +17 -2
  17. package/dist/types-ts4.5/editor-commands/transform-node-utils/utils.d.ts +5 -0
  18. package/package.json +3 -3
  19. package/dist/cjs/editor-commands/transform-node-utils/wrapIntoLayoutStep.js +0 -20
  20. package/dist/es2019/editor-commands/transform-node-utils/wrapIntoLayoutStep.js +0 -17
  21. package/dist/esm/editor-commands/transform-node-utils/wrapIntoLayoutStep.js +0 -14
  22. package/dist/types/editor-commands/transform-node-utils/wrapIntoLayoutStep.d.ts +0 -2
  23. package/dist/types-ts4.5/editor-commands/transform-node-utils/wrapIntoLayoutStep.d.ts +0 -2
@@ -15,7 +15,6 @@ import { wrapTextToCodeblockStep } from './steps/wrapTextToCodeblock';
15
15
  import { getNodeName, NODE_CATEGORY_BY_TYPE, toNodeTypeValue } from './types';
16
16
  import { unwrapExpandStep } from './unwrapExpandStep';
17
17
  import { unwrapStep } from './unwrapStep';
18
- import { wrapIntoLayoutStep } from './wrapIntoLayoutStep';
19
18
  import { wrapIntoListStep } from './wrapIntoListStep';
20
19
  import { wrapStep } from './wrapStep';
21
20
 
@@ -65,15 +64,15 @@ const TRANSFORM_STEPS_OVERRIDE = {
65
64
  paragraph: {
66
65
  paragraph: null,
67
66
  codeBlock: [wrapTextToCodeblockStep],
68
- layoutSection: [wrapIntoLayoutStep]
67
+ layoutSection: [wrapMixedContentStep]
69
68
  },
70
69
  heading: {
71
70
  codeBlock: [wrapTextToCodeblockStep],
72
- layoutSection: [wrapIntoLayoutStep]
71
+ layoutSection: [wrapMixedContentStep]
73
72
  },
74
73
  panel: {
75
74
  panel: null,
76
- layoutSection: [unwrapStep, wrapIntoLayoutStep],
75
+ layoutSection: [unwrapStep, wrapMixedContentStep],
77
76
  codeBlock: [unwrapStep, flattenStep, wrapStep],
78
77
  blockquote: [unwrapStep, wrapMixedContentStep],
79
78
  taskList: null,
@@ -85,7 +84,7 @@ const TRANSFORM_STEPS_OVERRIDE = {
85
84
  expand: null,
86
85
  panel: [unwrapExpandStep, wrapMixedContentStep],
87
86
  blockquote: [unwrapExpandStep, wrapMixedContentStep],
88
- layoutSection: [unwrapExpandStep, wrapIntoLayoutStep],
87
+ layoutSection: [unwrapExpandStep, wrapMixedContentStep],
89
88
  paragraph: [unwrapExpandStep],
90
89
  codeBlock: null,
91
90
  heading: null
@@ -103,7 +102,7 @@ const TRANSFORM_STEPS_OVERRIDE = {
103
102
  blockquote: null,
104
103
  expand: [wrapStep],
105
104
  nestedExpand: [wrapStep],
106
- layoutSection: [wrapIntoLayoutStep],
105
+ layoutSection: [wrapMixedContentStep],
107
106
  codeBlock: null,
108
107
  decisionList: [unwrapStep, wrapBlockquoteToDecisionListStep]
109
108
  },
@@ -121,34 +120,34 @@ const TRANSFORM_STEPS_OVERRIDE = {
121
120
  blockquote: [wrapStep],
122
121
  expand: [wrapStep],
123
122
  nestedExpand: [wrapStep],
124
- layoutSection: [wrapIntoLayoutStep],
123
+ layoutSection: [wrapMixedContentStep],
125
124
  panel: [wrapStep],
126
125
  heading: null
127
126
  },
128
127
  bulletList: {
129
128
  bulletList: null,
130
129
  codeBlock: null,
131
- layoutSection: [wrapIntoLayoutStep],
130
+ layoutSection: [wrapMixedContentStep],
132
131
  decisionList: [flattenListStep, listToDecisionListStep],
133
132
  heading: null
134
133
  },
135
134
  orderedList: {
136
135
  orderedList: null,
137
136
  codeBlock: null,
138
- layoutSection: [wrapIntoLayoutStep],
137
+ layoutSection: [wrapMixedContentStep],
139
138
  decisionList: [flattenListStep, listToDecisionListStep],
140
139
  heading: null
141
140
  },
142
141
  taskList: {
143
142
  blockquote: null,
144
143
  codeBlock: null,
145
- layoutSection: [wrapIntoLayoutStep],
144
+ layoutSection: [wrapMixedContentStep],
146
145
  decisionList: [flattenListStep, listToDecisionListStep],
147
146
  heading: null,
148
147
  taskList: null
149
148
  },
150
149
  table: {
151
- layoutSection: [wrapIntoLayoutStep],
150
+ layoutSection: [wrapMixedContentStep],
152
151
  blockquote: null,
153
152
  panel: null,
154
153
  codeBlock: null,
@@ -158,13 +157,13 @@ const TRANSFORM_STEPS_OVERRIDE = {
158
157
  decisionList: null
159
158
  },
160
159
  mediaSingle: {
161
- layoutSection: [wrapIntoLayoutStep],
160
+ layoutSection: [wrapMixedContentStep],
162
161
  codeBlock: null,
163
162
  decisionList: null,
164
163
  taskList: null
165
164
  },
166
165
  mediaGroup: {
167
- layoutSection: [wrapIntoLayoutStep],
166
+ layoutSection: [wrapMixedContentStep],
168
167
  codeBlock: null,
169
168
  decisionList: null,
170
169
  bulletList: null,
@@ -176,10 +175,10 @@ const TRANSFORM_STEPS_OVERRIDE = {
176
175
  bulletList: [decisionListToListStep],
177
176
  orderedList: [decisionListToListStep],
178
177
  taskList: [decisionListToListStep],
179
- layoutSection: [wrapIntoLayoutStep]
178
+ layoutSection: [wrapMixedContentStep]
180
179
  },
181
180
  blockCard: {
182
- layoutSection: [wrapIntoLayoutStep],
181
+ layoutSection: [wrapMixedContentStep],
183
182
  blockquote: null,
184
183
  codeBlock: null,
185
184
  orderedList: null,
@@ -188,7 +187,7 @@ const TRANSFORM_STEPS_OVERRIDE = {
188
187
  decisionList: null
189
188
  },
190
189
  embedCard: {
191
- layoutSection: [wrapIntoLayoutStep],
190
+ layoutSection: [wrapMixedContentStep],
192
191
  blockquote: null,
193
192
  panel: null,
194
193
  codeBlock: null,
@@ -198,7 +197,7 @@ const TRANSFORM_STEPS_OVERRIDE = {
198
197
  decisionList: null
199
198
  },
200
199
  extension: {
201
- layoutSection: [wrapIntoLayoutStep],
200
+ layoutSection: [wrapMixedContentStep],
202
201
  codeBlock: null,
203
202
  decisionList: null,
204
203
  taskList: null,
@@ -206,7 +205,7 @@ const TRANSFORM_STEPS_OVERRIDE = {
206
205
  bulletList: null
207
206
  },
208
207
  bodiedExtension: {
209
- layoutSection: [wrapIntoLayoutStep],
208
+ layoutSection: [wrapMixedContentStep],
210
209
  blockquote: null,
211
210
  expand: null,
212
211
  panel: null,
@@ -220,9 +219,7 @@ const TRANSFORM_STEPS_OVERRIDE = {
220
219
  // TODO: EDITOR-4140 - Implement multiple paragraphs/headings/codeblocks to heading transform
221
220
  heading: null,
222
221
  // TODO: EDITOR-4141 - Implement multiple codeblocks/headings to paragraph transform
223
- paragraph: null,
224
- // TODO: EDITOR-4138 - Implement multi content to layout transform
225
- layoutSection: undefined
222
+ paragraph: null
226
223
  }
227
224
  };
228
225
  const getTransformStepsForNodeTypes = (selectedNodeTypeName, targetNodeTypeName) => {
@@ -1,6 +1,16 @@
1
1
  import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
2
2
  import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
3
3
  import { CellSelection } from '@atlaskit/editor-tables';
4
+ import { NODE_CATEGORY_BY_TYPE } from './types';
5
+
6
+ /**
7
+ * Determines if a node is a text node (heading or paragraph).
8
+ * Text nodes can have their content converted to paragraphs when they can't be wrapped directly.
9
+ */
10
+ export const isTextNode = node => {
11
+ const category = NODE_CATEGORY_BY_TYPE[node.type.name];
12
+ return category === 'text';
13
+ };
4
14
  export const getSelectedNode = selection => {
5
15
  if (selection instanceof NodeSelection) {
6
16
  return {
@@ -1,14 +1,4 @@
1
- import { NODE_CATEGORY_BY_TYPE } from '../types';
2
-
3
- /**
4
- * Determines if a node is a text node (heading or paragraph).
5
- * Only text nodes should have their inline content extracted for decisionItem.
6
- * All other nodes should break out.
7
- */
8
- var isTextNode = function isTextNode(node) {
9
- var category = NODE_CATEGORY_BY_TYPE[node.type.name];
10
- return category === 'text';
11
- };
1
+ import { isTextNode } from '../utils';
12
2
 
13
3
  /**
14
4
  * Creates a decisionItem with the given inline content.
@@ -2,34 +2,7 @@ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
2
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
3
3
  import { removeDisallowedMarks } from '../marks';
4
4
  import { NODE_CATEGORY_BY_TYPE } from '../types';
5
- import { convertTextNodeToParagraph } from '../utils';
6
-
7
- /**
8
- * Determines if a node is a text node (heading or paragraph).
9
- * Text nodes can have their content converted to paragraphs when they can't be wrapped directly.
10
- */
11
- var isTextNode = function isTextNode(node) {
12
- var category = NODE_CATEGORY_BY_TYPE[node.type.name];
13
- return category === 'text';
14
- };
15
-
16
- /**
17
- * Determines if a node can be wrapped in the target container type, removes block marks from the node during check.
18
- * Uses the schema's validContent to check if the target container can hold this node.
19
- *
20
- * Note: What can be wrapped depends on the target container type - for example:
21
- * - Tables and media CAN go inside expand nodes
22
- * - Tables CANNOT go inside panels or blockquotes
23
- */
24
- var canWrapInTarget = function canWrapInTarget(node, targetNodeType, targetNodeTypeName) {
25
- // Same-type containers should break out as separate containers
26
- if (node.type.name === targetNodeTypeName) {
27
- return false;
28
- }
29
-
30
- // Use the schema to determine if this node can be contained in the target
31
- return targetNodeType.validContent(Fragment.from(removeDisallowedMarks([node], targetNodeType)));
32
- };
5
+ import { convertTextNodeToParagraph, isTextNode } from '../utils';
33
6
 
34
7
  /**
35
8
  * Handles the edge case where transforming from a container to another container results in
@@ -69,18 +42,33 @@ var handleEmptyContainerEdgeCase = function handleEmptyContainerEdgeCase(result,
69
42
  * - Same-type containers break out as separate containers (preserved as-is)
70
43
  * - NestedExpands break out as regular expands (converted since nestedExpand can't exist outside expand)
71
44
  * - Container structures that can't be nested in target break out (not flattened)
72
- * - Text/list nodes that can't be wrapped are flattened and merged into the container
45
+ * - Text/list nodes that can't be wrapped are converted to paragraphs and merged into the container
73
46
  * - Atomic nodes (tables, media, macros) break out
74
47
  *
48
+ * Special handling for layouts:
49
+ * - Layout sections break out as separate layouts (preserved as-is, not wrapped)
50
+ * - Other nodes (including headings, paragraphs, lists) are wrapped into layout columns within a layout section
51
+ * - Layouts always require layoutColumns as children (never paragraphs directly)
52
+ * - Layout columns can contain most block content including headings, paragraphs, lists, etc.
53
+ *
54
+ * Edge case handling:
55
+ * - For regular containers: If all content breaks out (container → container transform with no
56
+ * valid children), an empty container with a paragraph is created to ensure the target type exists
57
+ * - For layouts: Edge case handling is skipped because layouts require columns, not direct paragraphs.
58
+ * If all content breaks out, only the broken-out nodes are returned (no empty layout created)
59
+ *
75
60
  * What can be wrapped depends on the target container's schema:
76
61
  * - expand → panel: tables break out, nestedExpands convert to expands and break out
77
- * - expand → blockquote: tables/media break out, nestedExpands convert to expands and break out
62
+ * - expand → blockquote: tables/media break out, nestedExpands convert to expands and break out, headings converted to paragraphs
78
63
  * - expand → expand: tables/media stay inside (expands can contain them)
64
+ * - multi → layoutSection: layout sections break out, headings/paragraphs/lists wrapped into layout columns
79
65
  *
80
66
  * Example: expand(p('a'), table(), p('b')) → panel: [panel(p('a')), table(), panel(p('b'))]
81
67
  * Example: expand(p('a'), panel(p('x')), p('b')) → panel: [panel(p('a')), panel(p('x')), panel(p('b'))]
82
68
  * Example: expand(p('a'), nestedExpand({title: 'inner'})(p('x')), p('b')) → panel: [panel(p('a')), expand({title: 'inner'})(p('x')), panel(p('b'))]
83
69
  * Example: expand(nestedExpand()(p())) → panel: [panel(), expand()(p())] (empty panel when all content breaks out)
70
+ * Example: [p('a'), layoutSection(...), p('b')] → layoutSection: [layoutSection(layoutColumn(p('a'))), layoutSection(...), layoutSection(layoutColumn(p('b')))]
71
+ * Example: [h1('heading'), p('para')] → layoutSection: [layoutSection(layoutColumn(h1('heading'), p('para')))] (headings stay as headings in layouts)
84
72
  */
85
73
  export var wrapMixedContentStep = function wrapMixedContentStep(nodes, context) {
86
74
  var schema = context.schema,
@@ -90,49 +78,78 @@ export var wrapMixedContentStep = function wrapMixedContentStep(nodes, context)
90
78
  if (!targetNodeType) {
91
79
  return nodes;
92
80
  }
81
+ var isLayout = targetNodeTypeName === 'layoutSection';
82
+ var _schema$nodes = schema.nodes,
83
+ layoutSection = _schema$nodes.layoutSection,
84
+ layoutColumn = _schema$nodes.layoutColumn;
93
85
  var result = [];
94
86
  var currentContainerContent = [];
95
87
  var hasCreatedContainer = false;
96
88
  var flushCurrentContainer = function flushCurrentContainer() {
97
- if (currentContainerContent.length > 0) {
98
- var containerNode = targetNodeType.createAndFill({}, Fragment.fromArray(currentContainerContent));
99
- if (containerNode) {
100
- result.push(containerNode);
89
+ if (currentContainerContent.length === 0) {
90
+ return;
91
+ }
92
+ if (isLayout) {
93
+ // For layouts, create layoutSection with two layoutColumns
94
+ var columnOne = layoutColumn.createAndFill({}, removeDisallowedMarks(currentContainerContent, layoutColumn));
95
+ var columnTwo = layoutColumn.createAndFill();
96
+ if (!columnOne || !columnTwo) {
97
+ currentContainerContent = [];
98
+ return;
99
+ }
100
+ var layout = layoutSection.createAndFill({}, [columnOne, columnTwo]);
101
+ if (layout) {
102
+ result.push(layout);
101
103
  hasCreatedContainer = true;
102
104
  }
103
105
  currentContainerContent = [];
106
+ return;
104
107
  }
108
+
109
+ // For regular containers, create directly
110
+ var containerNode = targetNodeType.createAndFill({}, currentContainerContent);
111
+ if (containerNode) {
112
+ result.push(containerNode);
113
+ hasCreatedContainer = true;
114
+ }
115
+ currentContainerContent = [];
105
116
  };
106
- nodes.forEach(function (node) {
107
- if (canWrapInTarget(node, targetNodeType, targetNodeTypeName)) {
117
+ var processNode = function processNode(node) {
118
+ var validationType = isLayout ? layoutColumn : targetNodeType;
119
+ var canWrapNode = validationType.validContent(Fragment.from(removeDisallowedMarks([node], validationType)));
120
+
121
+ // Node can be wrapped - add to current container content
122
+ if (canWrapNode) {
108
123
  var _currentContainerCont;
109
- // Node can be wrapped - add to current container content
110
124
  // remove marks from node as nested nodes don't usually support block marks
111
- (_currentContainerCont = currentContainerContent).push.apply(_currentContainerCont, _toConsumableArray(removeDisallowedMarks([node], targetNodeType)));
112
- } else if (node.type.name === targetNodeTypeName) {
113
- // Same-type container - breaks out as a separate container (preserved as-is)
114
- // This handles: "If there's a panel in the expand, it breaks out into a separate panel"
115
- flushCurrentContainer();
116
- result.push(node);
117
- } else if (isTextNode(node)) {
118
- // Text node (heading, paragraph) that can't be wrapped - convert to paragraph
119
- // Example: heading can't go in blockquote, so convert to paragraph with same content
125
+ (_currentContainerCont = currentContainerContent).push.apply(_currentContainerCont, _toConsumableArray(removeDisallowedMarks([node], validationType)));
126
+ return;
127
+ }
128
+
129
+ // Text node (heading, paragraph) that can't be wrapped - convert to paragraph
130
+ // Example: heading can't go in blockquote, so convert to paragraph with same content
131
+ if (isTextNode(node)) {
120
132
  var paragraph = convertTextNodeToParagraph(node, schema);
121
133
  if (paragraph) {
122
134
  currentContainerContent.push(paragraph);
123
135
  }
124
- } else {
125
- // All other nodes that cannot be wrapped (lists, containers, tables, media, macros) - break out
126
- // This includes list nodes like taskList that can't be placed in certain containers
127
- flushCurrentContainer();
128
- result.push(node);
136
+ return;
129
137
  }
130
- });
138
+
139
+ // All other nodes that cannot be wrapped in the target node - break out
140
+ // Examples: same-type containers, tables in panels, layoutSections in layouts
141
+ flushCurrentContainer();
142
+ result.push(node);
143
+ };
144
+ nodes.forEach(processNode);
131
145
 
132
146
  // Flush any remaining content into a container
133
147
  flushCurrentContainer();
134
148
 
135
- // Handle edge case: create empty container if all content broke out
149
+ // Skip edge case handling for layouts since layouts always have columns
150
+ if (isLayout) {
151
+ return result.length > 0 ? result : nodes;
152
+ }
136
153
  var finalResult = handleEmptyContainerEdgeCase(result, hasCreatedContainer, fromNode, targetNodeType, targetNodeTypeName, schema);
137
154
  return finalResult.length > 0 ? finalResult : nodes;
138
155
  };
@@ -15,7 +15,6 @@ import { wrapTextToCodeblockStep } from './steps/wrapTextToCodeblock';
15
15
  import { getNodeName, NODE_CATEGORY_BY_TYPE, toNodeTypeValue } from './types';
16
16
  import { unwrapExpandStep } from './unwrapExpandStep';
17
17
  import { unwrapStep } from './unwrapStep';
18
- import { wrapIntoLayoutStep } from './wrapIntoLayoutStep';
19
18
  import { wrapIntoListStep } from './wrapIntoListStep';
20
19
  import { wrapStep } from './wrapStep';
21
20
 
@@ -65,15 +64,15 @@ var TRANSFORM_STEPS_OVERRIDE = {
65
64
  paragraph: {
66
65
  paragraph: null,
67
66
  codeBlock: [wrapTextToCodeblockStep],
68
- layoutSection: [wrapIntoLayoutStep]
67
+ layoutSection: [wrapMixedContentStep]
69
68
  },
70
69
  heading: {
71
70
  codeBlock: [wrapTextToCodeblockStep],
72
- layoutSection: [wrapIntoLayoutStep]
71
+ layoutSection: [wrapMixedContentStep]
73
72
  },
74
73
  panel: {
75
74
  panel: null,
76
- layoutSection: [unwrapStep, wrapIntoLayoutStep],
75
+ layoutSection: [unwrapStep, wrapMixedContentStep],
77
76
  codeBlock: [unwrapStep, flattenStep, wrapStep],
78
77
  blockquote: [unwrapStep, wrapMixedContentStep],
79
78
  taskList: null,
@@ -85,7 +84,7 @@ var TRANSFORM_STEPS_OVERRIDE = {
85
84
  expand: null,
86
85
  panel: [unwrapExpandStep, wrapMixedContentStep],
87
86
  blockquote: [unwrapExpandStep, wrapMixedContentStep],
88
- layoutSection: [unwrapExpandStep, wrapIntoLayoutStep],
87
+ layoutSection: [unwrapExpandStep, wrapMixedContentStep],
89
88
  paragraph: [unwrapExpandStep],
90
89
  codeBlock: null,
91
90
  heading: null
@@ -103,7 +102,7 @@ var TRANSFORM_STEPS_OVERRIDE = {
103
102
  blockquote: null,
104
103
  expand: [wrapStep],
105
104
  nestedExpand: [wrapStep],
106
- layoutSection: [wrapIntoLayoutStep],
105
+ layoutSection: [wrapMixedContentStep],
107
106
  codeBlock: null,
108
107
  decisionList: [unwrapStep, wrapBlockquoteToDecisionListStep]
109
108
  },
@@ -121,34 +120,34 @@ var TRANSFORM_STEPS_OVERRIDE = {
121
120
  blockquote: [wrapStep],
122
121
  expand: [wrapStep],
123
122
  nestedExpand: [wrapStep],
124
- layoutSection: [wrapIntoLayoutStep],
123
+ layoutSection: [wrapMixedContentStep],
125
124
  panel: [wrapStep],
126
125
  heading: null
127
126
  },
128
127
  bulletList: {
129
128
  bulletList: null,
130
129
  codeBlock: null,
131
- layoutSection: [wrapIntoLayoutStep],
130
+ layoutSection: [wrapMixedContentStep],
132
131
  decisionList: [flattenListStep, listToDecisionListStep],
133
132
  heading: null
134
133
  },
135
134
  orderedList: {
136
135
  orderedList: null,
137
136
  codeBlock: null,
138
- layoutSection: [wrapIntoLayoutStep],
137
+ layoutSection: [wrapMixedContentStep],
139
138
  decisionList: [flattenListStep, listToDecisionListStep],
140
139
  heading: null
141
140
  },
142
141
  taskList: {
143
142
  blockquote: null,
144
143
  codeBlock: null,
145
- layoutSection: [wrapIntoLayoutStep],
144
+ layoutSection: [wrapMixedContentStep],
146
145
  decisionList: [flattenListStep, listToDecisionListStep],
147
146
  heading: null,
148
147
  taskList: null
149
148
  },
150
149
  table: {
151
- layoutSection: [wrapIntoLayoutStep],
150
+ layoutSection: [wrapMixedContentStep],
152
151
  blockquote: null,
153
152
  panel: null,
154
153
  codeBlock: null,
@@ -158,13 +157,13 @@ var TRANSFORM_STEPS_OVERRIDE = {
158
157
  decisionList: null
159
158
  },
160
159
  mediaSingle: {
161
- layoutSection: [wrapIntoLayoutStep],
160
+ layoutSection: [wrapMixedContentStep],
162
161
  codeBlock: null,
163
162
  decisionList: null,
164
163
  taskList: null
165
164
  },
166
165
  mediaGroup: {
167
- layoutSection: [wrapIntoLayoutStep],
166
+ layoutSection: [wrapMixedContentStep],
168
167
  codeBlock: null,
169
168
  decisionList: null,
170
169
  bulletList: null,
@@ -176,10 +175,10 @@ var TRANSFORM_STEPS_OVERRIDE = {
176
175
  bulletList: [decisionListToListStep],
177
176
  orderedList: [decisionListToListStep],
178
177
  taskList: [decisionListToListStep],
179
- layoutSection: [wrapIntoLayoutStep]
178
+ layoutSection: [wrapMixedContentStep]
180
179
  },
181
180
  blockCard: {
182
- layoutSection: [wrapIntoLayoutStep],
181
+ layoutSection: [wrapMixedContentStep],
183
182
  blockquote: null,
184
183
  codeBlock: null,
185
184
  orderedList: null,
@@ -188,7 +187,7 @@ var TRANSFORM_STEPS_OVERRIDE = {
188
187
  decisionList: null
189
188
  },
190
189
  embedCard: {
191
- layoutSection: [wrapIntoLayoutStep],
190
+ layoutSection: [wrapMixedContentStep],
192
191
  blockquote: null,
193
192
  panel: null,
194
193
  codeBlock: null,
@@ -198,7 +197,7 @@ var TRANSFORM_STEPS_OVERRIDE = {
198
197
  decisionList: null
199
198
  },
200
199
  extension: {
201
- layoutSection: [wrapIntoLayoutStep],
200
+ layoutSection: [wrapMixedContentStep],
202
201
  codeBlock: null,
203
202
  decisionList: null,
204
203
  taskList: null,
@@ -206,7 +205,7 @@ var TRANSFORM_STEPS_OVERRIDE = {
206
205
  bulletList: null
207
206
  },
208
207
  bodiedExtension: {
209
- layoutSection: [wrapIntoLayoutStep],
208
+ layoutSection: [wrapMixedContentStep],
210
209
  blockquote: null,
211
210
  expand: null,
212
211
  panel: null,
@@ -220,9 +219,7 @@ var TRANSFORM_STEPS_OVERRIDE = {
220
219
  // TODO: EDITOR-4140 - Implement multiple paragraphs/headings/codeblocks to heading transform
221
220
  heading: null,
222
221
  // TODO: EDITOR-4141 - Implement multiple codeblocks/headings to paragraph transform
223
- paragraph: null,
224
- // TODO: EDITOR-4138 - Implement multi content to layout transform
225
- layoutSection: undefined
222
+ paragraph: null
226
223
  }
227
224
  };
228
225
  var getTransformStepsForNodeTypes = function getTransformStepsForNodeTypes(selectedNodeTypeName, targetNodeTypeName) {
@@ -1,6 +1,16 @@
1
1
  import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
2
2
  import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
3
3
  import { CellSelection } from '@atlaskit/editor-tables';
4
+ import { NODE_CATEGORY_BY_TYPE } from './types';
5
+
6
+ /**
7
+ * Determines if a node is a text node (heading or paragraph).
8
+ * Text nodes can have their content converted to paragraphs when they can't be wrapped directly.
9
+ */
10
+ export var isTextNode = function isTextNode(node) {
11
+ var category = NODE_CATEGORY_BY_TYPE[node.type.name];
12
+ return category === 'text';
13
+ };
4
14
  export var getSelectedNode = function getSelectedNode(selection) {
5
15
  if (selection instanceof NodeSelection) {
6
16
  return {
@@ -5,17 +5,32 @@ import type { TransformStep } from '../types';
5
5
  * - Same-type containers break out as separate containers (preserved as-is)
6
6
  * - NestedExpands break out as regular expands (converted since nestedExpand can't exist outside expand)
7
7
  * - Container structures that can't be nested in target break out (not flattened)
8
- * - Text/list nodes that can't be wrapped are flattened and merged into the container
8
+ * - Text/list nodes that can't be wrapped are converted to paragraphs and merged into the container
9
9
  * - Atomic nodes (tables, media, macros) break out
10
10
  *
11
+ * Special handling for layouts:
12
+ * - Layout sections break out as separate layouts (preserved as-is, not wrapped)
13
+ * - Other nodes (including headings, paragraphs, lists) are wrapped into layout columns within a layout section
14
+ * - Layouts always require layoutColumns as children (never paragraphs directly)
15
+ * - Layout columns can contain most block content including headings, paragraphs, lists, etc.
16
+ *
17
+ * Edge case handling:
18
+ * - For regular containers: If all content breaks out (container → container transform with no
19
+ * valid children), an empty container with a paragraph is created to ensure the target type exists
20
+ * - For layouts: Edge case handling is skipped because layouts require columns, not direct paragraphs.
21
+ * If all content breaks out, only the broken-out nodes are returned (no empty layout created)
22
+ *
11
23
  * What can be wrapped depends on the target container's schema:
12
24
  * - expand → panel: tables break out, nestedExpands convert to expands and break out
13
- * - expand → blockquote: tables/media break out, nestedExpands convert to expands and break out
25
+ * - expand → blockquote: tables/media break out, nestedExpands convert to expands and break out, headings converted to paragraphs
14
26
  * - expand → expand: tables/media stay inside (expands can contain them)
27
+ * - multi → layoutSection: layout sections break out, headings/paragraphs/lists wrapped into layout columns
15
28
  *
16
29
  * Example: expand(p('a'), table(), p('b')) → panel: [panel(p('a')), table(), panel(p('b'))]
17
30
  * Example: expand(p('a'), panel(p('x')), p('b')) → panel: [panel(p('a')), panel(p('x')), panel(p('b'))]
18
31
  * Example: expand(p('a'), nestedExpand({title: 'inner'})(p('x')), p('b')) → panel: [panel(p('a')), expand({title: 'inner'})(p('x')), panel(p('b'))]
19
32
  * Example: expand(nestedExpand()(p())) → panel: [panel(), expand()(p())] (empty panel when all content breaks out)
33
+ * Example: [p('a'), layoutSection(...), p('b')] → layoutSection: [layoutSection(layoutColumn(p('a'))), layoutSection(...), layoutSection(layoutColumn(p('b')))]
34
+ * Example: [h1('heading'), p('para')] → layoutSection: [layoutSection(layoutColumn(h1('heading'), p('para')))] (headings stay as headings in layouts)
20
35
  */
21
36
  export declare const wrapMixedContentStep: TransformStep;
@@ -2,6 +2,11 @@ import type { NodeRange, Node as PMNode, Schema } from '@atlaskit/editor-prosemi
2
2
  import type { Selection } from '@atlaskit/editor-prosemirror/state';
3
3
  import { type ContentNodeWithPos } from '@atlaskit/editor-prosemirror/utils';
4
4
  import type { NodeTypeName } from './types';
5
+ /**
6
+ * Determines if a node is a text node (heading or paragraph).
7
+ * Text nodes can have their content converted to paragraphs when they can't be wrapped directly.
8
+ */
9
+ export declare const isTextNode: (node: PMNode) => boolean;
5
10
  export declare const getSelectedNode: (selection: Selection) => ContentNodeWithPos | undefined;
6
11
  export declare const getTargetNodeTypeNameInContext: (nodeTypeName: NodeTypeName | null, isNested?: boolean, parentNode?: PMNode) => NodeTypeName | null;
7
12
  /**
@@ -5,17 +5,32 @@ import type { TransformStep } from '../types';
5
5
  * - Same-type containers break out as separate containers (preserved as-is)
6
6
  * - NestedExpands break out as regular expands (converted since nestedExpand can't exist outside expand)
7
7
  * - Container structures that can't be nested in target break out (not flattened)
8
- * - Text/list nodes that can't be wrapped are flattened and merged into the container
8
+ * - Text/list nodes that can't be wrapped are converted to paragraphs and merged into the container
9
9
  * - Atomic nodes (tables, media, macros) break out
10
10
  *
11
+ * Special handling for layouts:
12
+ * - Layout sections break out as separate layouts (preserved as-is, not wrapped)
13
+ * - Other nodes (including headings, paragraphs, lists) are wrapped into layout columns within a layout section
14
+ * - Layouts always require layoutColumns as children (never paragraphs directly)
15
+ * - Layout columns can contain most block content including headings, paragraphs, lists, etc.
16
+ *
17
+ * Edge case handling:
18
+ * - For regular containers: If all content breaks out (container → container transform with no
19
+ * valid children), an empty container with a paragraph is created to ensure the target type exists
20
+ * - For layouts: Edge case handling is skipped because layouts require columns, not direct paragraphs.
21
+ * If all content breaks out, only the broken-out nodes are returned (no empty layout created)
22
+ *
11
23
  * What can be wrapped depends on the target container's schema:
12
24
  * - expand → panel: tables break out, nestedExpands convert to expands and break out
13
- * - expand → blockquote: tables/media break out, nestedExpands convert to expands and break out
25
+ * - expand → blockquote: tables/media break out, nestedExpands convert to expands and break out, headings converted to paragraphs
14
26
  * - expand → expand: tables/media stay inside (expands can contain them)
27
+ * - multi → layoutSection: layout sections break out, headings/paragraphs/lists wrapped into layout columns
15
28
  *
16
29
  * Example: expand(p('a'), table(), p('b')) → panel: [panel(p('a')), table(), panel(p('b'))]
17
30
  * Example: expand(p('a'), panel(p('x')), p('b')) → panel: [panel(p('a')), panel(p('x')), panel(p('b'))]
18
31
  * Example: expand(p('a'), nestedExpand({title: 'inner'})(p('x')), p('b')) → panel: [panel(p('a')), expand({title: 'inner'})(p('x')), panel(p('b'))]
19
32
  * Example: expand(nestedExpand()(p())) → panel: [panel(), expand()(p())] (empty panel when all content breaks out)
33
+ * Example: [p('a'), layoutSection(...), p('b')] → layoutSection: [layoutSection(layoutColumn(p('a'))), layoutSection(...), layoutSection(layoutColumn(p('b')))]
34
+ * Example: [h1('heading'), p('para')] → layoutSection: [layoutSection(layoutColumn(h1('heading'), p('para')))] (headings stay as headings in layouts)
20
35
  */
21
36
  export declare const wrapMixedContentStep: TransformStep;
@@ -2,6 +2,11 @@ import type { NodeRange, Node as PMNode, Schema } from '@atlaskit/editor-prosemi
2
2
  import type { Selection } from '@atlaskit/editor-prosemirror/state';
3
3
  import { type ContentNodeWithPos } from '@atlaskit/editor-prosemirror/utils';
4
4
  import type { NodeTypeName } from './types';
5
+ /**
6
+ * Determines if a node is a text node (heading or paragraph).
7
+ * Text nodes can have their content converted to paragraphs when they can't be wrapped directly.
8
+ */
9
+ export declare const isTextNode: (node: PMNode) => boolean;
5
10
  export declare const getSelectedNode: (selection: Selection) => ContentNodeWithPos | undefined;
6
11
  export declare const getTargetNodeTypeNameInContext: (nodeTypeName: NodeTypeName | null, isNested?: boolean, parentNode?: PMNode) => NodeTypeName | null;
7
12
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-block-menu",
3
- "version": "6.0.5",
3
+ "version": "6.0.6",
4
4
  "description": "BlockMenu plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -44,12 +44,12 @@
44
44
  "@atlaskit/platform-feature-flags": "^1.1.0",
45
45
  "@atlaskit/platform-feature-flags-react": "^0.4.0",
46
46
  "@atlaskit/primitives": "^17.0.0",
47
- "@atlaskit/tmp-editor-statsig": "^16.3.0",
47
+ "@atlaskit/tmp-editor-statsig": "^16.4.0",
48
48
  "@atlaskit/tokens": "^9.0.0",
49
49
  "@babel/runtime": "^7.0.0"
50
50
  },
51
51
  "peerDependencies": {
52
- "@atlaskit/editor-common": "^111.1.0",
52
+ "@atlaskit/editor-common": "^111.2.0",
53
53
  "react": "^18.2.0",
54
54
  "react-intl-next": "npm:react-intl@^5.18.1"
55
55
  },