@atlaskit/editor-plugin-block-menu 6.0.7 → 6.0.8

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 (26) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/editor-commands/transform-node-utils/flattenStep.js +0 -10
  3. package/dist/cjs/editor-commands/transform-node-utils/steps/applyTargetTextTypeStep.js +22 -18
  4. package/dist/cjs/editor-commands/transform-node-utils/steps/wrapMixedContentStep.js +79 -45
  5. package/dist/cjs/editor-commands/transform-node-utils/transform.js +13 -15
  6. package/dist/cjs/editor-commands/transformNode.js +8 -2
  7. package/dist/es2019/editor-commands/transform-node-utils/flattenStep.js +0 -8
  8. package/dist/es2019/editor-commands/transform-node-utils/steps/applyTargetTextTypeStep.js +22 -18
  9. package/dist/es2019/editor-commands/transform-node-utils/steps/wrapMixedContentStep.js +79 -45
  10. package/dist/es2019/editor-commands/transform-node-utils/transform.js +13 -15
  11. package/dist/es2019/editor-commands/transformNode.js +8 -2
  12. package/dist/esm/editor-commands/transform-node-utils/flattenStep.js +0 -10
  13. package/dist/esm/editor-commands/transform-node-utils/steps/applyTargetTextTypeStep.js +22 -18
  14. package/dist/esm/editor-commands/transform-node-utils/steps/wrapMixedContentStep.js +80 -46
  15. package/dist/esm/editor-commands/transform-node-utils/transform.js +13 -15
  16. package/dist/esm/editor-commands/transformNode.js +8 -2
  17. package/dist/types/editor-commands/transform-node-utils/steps/applyTargetTextTypeStep.d.ts +10 -7
  18. package/dist/types/editor-commands/transform-node-utils/steps/wrapMixedContentStep.d.ts +3 -0
  19. package/dist/types-ts4.5/editor-commands/transform-node-utils/steps/applyTargetTextTypeStep.d.ts +10 -7
  20. package/dist/types-ts4.5/editor-commands/transform-node-utils/steps/wrapMixedContentStep.d.ts +3 -0
  21. package/package.json +2 -2
  22. package/dist/cjs/editor-commands/transform-node-utils/steps/wrapTextToCodeblock.js +0 -21
  23. package/dist/es2019/editor-commands/transform-node-utils/steps/wrapTextToCodeblock.js +0 -18
  24. package/dist/esm/editor-commands/transform-node-utils/steps/wrapTextToCodeblock.js +0 -16
  25. package/dist/types/editor-commands/transform-node-utils/steps/wrapTextToCodeblock.d.ts +0 -9
  26. package/dist/types-ts4.5/editor-commands/transform-node-utils/steps/wrapTextToCodeblock.d.ts +0 -9
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @atlaskit/editor-plugin-block-menu
2
2
 
3
+ ## 6.0.8
4
+
5
+ ### Patch Changes
6
+
7
+ - [`bdcaf574d7d2d`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/bdcaf574d7d2d) -
8
+ Fix transformation of multiple headings and paragraphs to codeblocks
9
+ - [`68ebba7ccdc1b`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/68ebba7ccdc1b) -
10
+ [EDITOR-4157] Fix editor freeze when transforming mediaSingle at bottom of document
11
+ - [`fef9134c6feb5`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/fef9134c6feb5) -
12
+ [ux] Implement multiselect transformations for text (heading, paragraph) nodes
13
+ - Updated dependencies
14
+
3
15
  ## 6.0.7
4
16
 
5
17
  ### Patch Changes
@@ -12,16 +12,6 @@ var flattenStep = exports.flattenStep = function flattenStep(nodes, context) {
12
12
  if (!targetNodeType || !paragraph) {
13
13
  return nodes;
14
14
  }
15
-
16
- // TODO: EDITOR-2920 - Implement flattening logic.
17
- var isTargetCodeBlock = targetNodeTypeName === 'codeBlock';
18
- if (isTargetCodeBlock) {
19
- // This strips explicitly text nodes
20
- var codeBlockContent = nodes.map(function (node) {
21
- return node.content.textBetween(0, node.content.size, '\n');
22
- }).join('\n');
23
- return [schema.nodes.codeBlock.create({}, schema.text(codeBlockContent))];
24
- }
25
15
  return nodes.map(function (node) {
26
16
  var isValidWithin = targetNodeType.validContent(node.content);
27
17
  if (!isValidWithin) {
@@ -5,18 +5,21 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.applyTargetTextTypeStep = void 0;
7
7
  /**
8
- * Applies target text type conversion. If the target type is a heading, converts textblock nodes
9
- * (paragraphs, headings) to heading nodes with the specified level. Otherwise, leaves nodes unchanged.
10
- * Non-textblock nodes are always left unchanged.
8
+ * Applies target text type conversion. Converts textblock nodes to the target text type
9
+ * (paragraph or heading). Non-textblock nodes are left unchanged.
11
10
  *
12
11
  * @example
13
12
  * Input:
14
- * - paragraph "Heading 1"
15
- * - paragraph "Heading 2"
13
+ * - paragraph "Text 1"
14
+ * - paragraph "Text 2"
16
15
  *
17
16
  * Output (with target: heading, level: 2):
18
- * - heading (level: 2) "Heading 1"
19
- * - heading (level: 2) "Heading 2"
17
+ * - heading (level: 2) "Text 1"
18
+ * - heading (level: 2) "Text 2"
19
+ *
20
+ * Output (with target: paragraph):
21
+ * - paragraph "Text 1"
22
+ * - paragraph "Text 2"
20
23
  *
21
24
  * @param nodes
22
25
  * @param context
@@ -26,23 +29,24 @@ var applyTargetTextTypeStep = exports.applyTargetTextTypeStep = function applyTa
26
29
  var schema = context.schema,
27
30
  targetNodeTypeName = context.targetNodeTypeName,
28
31
  targetAttrs = context.targetAttrs;
29
- if (targetNodeTypeName !== 'heading') {
32
+ if (targetNodeTypeName !== 'heading' && targetNodeTypeName !== 'paragraph') {
30
33
  return nodes;
31
34
  }
32
- var headingType = schema.nodes.heading;
33
- if (!headingType) {
35
+ var targetType = schema.nodes[targetNodeTypeName];
36
+ if (!targetType) {
34
37
  return nodes;
35
38
  }
36
-
37
- // Default to level 1 if no level is specified
38
- // The level should ideally come from targetAttrs, but if not available, use default
39
- var headingLevel = typeof (targetAttrs === null || targetAttrs === void 0 ? void 0 : targetAttrs.level) === 'number' ? targetAttrs.level : 1;
40
39
  return nodes.map(function (node) {
41
40
  if (node.isTextblock) {
42
- // Convert textblock nodes (paragraphs, headings) to heading with specified level
43
- return headingType.create({
44
- level: headingLevel
45
- }, node.content, node.marks);
41
+ // Convert textblock nodes to the target type with content preserved
42
+ var attrs = {};
43
+ if (targetNodeTypeName === 'heading') {
44
+ var level = typeof (targetAttrs === null || targetAttrs === void 0 ? void 0 : targetAttrs.level) === 'number' ? targetAttrs.level : 1;
45
+ attrs = {
46
+ level: level
47
+ };
48
+ }
49
+ return targetType.create(attrs, node.content, node.marks);
46
50
  }
47
51
  // Non-textblock nodes are left unchanged
48
52
  return node;
@@ -10,6 +10,34 @@ var _model = require("@atlaskit/editor-prosemirror/model");
10
10
  var _marks = require("../marks");
11
11
  var _types = require("../types");
12
12
  var _utils = require("../utils");
13
+ /**
14
+ * Creates a layout section with two columns, where the first column contains the provided content.
15
+ */
16
+ var createLayoutSection = function createLayoutSection(content, layoutSection, layoutColumn) {
17
+ var columnOne = layoutColumn.createAndFill({}, (0, _marks.removeDisallowedMarks)(content, layoutColumn));
18
+ var columnTwo = layoutColumn.createAndFill();
19
+ if (!columnOne || !columnTwo) {
20
+ return null;
21
+ }
22
+ return layoutSection.createAndFill({}, [columnOne, columnTwo]);
23
+ };
24
+
25
+ /**
26
+ * Creates a container with text content (for codeblocks).
27
+ */
28
+ var createTextContentContainer = function createTextContentContainer(textContentArray, targetNodeType, schema) {
29
+ var textContent = textContentArray.join('\n');
30
+ var textNode = textContent ? schema.text(textContent) : null;
31
+ return targetNodeType.createAndFill({}, textNode);
32
+ };
33
+
34
+ /**
35
+ * Creates a regular container with node content.
36
+ */
37
+ var createNodeContentContainer = function createNodeContentContainer(nodeContent, targetNodeType) {
38
+ return targetNodeType.createAndFill({}, nodeContent);
39
+ };
40
+
13
41
  /**
14
42
  * Handles the edge case where transforming from a container to another container results in
15
43
  * all content breaking out (no valid children for the target). In this case, creates an empty
@@ -34,12 +62,16 @@ var handleEmptyContainerEdgeCase = function handleEmptyContainerEdgeCase(result,
34
62
  // (meaning there were no valid children that could be wrapped)
35
63
  var allContentBrokeOut = !hasCreatedContainer && result.length > 0;
36
64
  var shouldCreateEmptyTarget = isFromContainer && isTargetContainer && allContentBrokeOut;
37
- if (shouldCreateEmptyTarget) {
38
- var emptyParagraph = schema.nodes.paragraph.create();
39
- var emptyContainer = targetNodeType.create({}, emptyParagraph);
40
- return [emptyContainer].concat((0, _toConsumableArray2.default)(result));
65
+ if (!shouldCreateEmptyTarget) {
66
+ return result;
67
+ }
68
+ if (targetNodeTypeName === schema.nodes.codeBlock.name) {
69
+ var emptyCodeBlock = createTextContentContainer([], schema.nodes.codeBlock, schema);
70
+ return emptyCodeBlock ? [emptyCodeBlock].concat((0, _toConsumableArray2.default)(result)) : result;
41
71
  }
42
- return result;
72
+ var emptyParagraph = schema.nodes.paragraph.create();
73
+ var emptyContainer = targetNodeType.create({}, emptyParagraph);
74
+ return [emptyContainer].concat((0, _toConsumableArray2.default)(result));
43
75
  };
44
76
 
45
77
  /**
@@ -57,6 +89,9 @@ var handleEmptyContainerEdgeCase = function handleEmptyContainerEdgeCase(result,
57
89
  * - Layouts always require layoutColumns as children (never paragraphs directly)
58
90
  * - Layout columns can contain most block content including headings, paragraphs, lists, etc.
59
91
  *
92
+ * Special handling for codeblocks:
93
+ * - Text nodes are converted to plain text and added to the codeblock
94
+ *
60
95
  * Edge case handling:
61
96
  * - For regular containers: If all content breaks out (container → container transform with no
62
97
  * valid children), an empty container with a paragraph is created to ensure the target type exists
@@ -85,6 +120,7 @@ var wrapMixedContentStep = exports.wrapMixedContentStep = function wrapMixedCont
85
120
  return nodes;
86
121
  }
87
122
  var isLayout = targetNodeTypeName === 'layoutSection';
123
+ var isCodeblock = targetNodeTypeName === 'codeBlock';
88
124
  var _schema$nodes = schema.nodes,
89
125
  layoutSection = _schema$nodes.layoutSection,
90
126
  layoutColumn = _schema$nodes.layoutColumn;
@@ -95,64 +131,62 @@ var wrapMixedContentStep = exports.wrapMixedContentStep = function wrapMixedCont
95
131
  if (currentContainerContent.length === 0) {
96
132
  return;
97
133
  }
134
+ var container = null;
98
135
  if (isLayout) {
99
- // For layouts, create layoutSection with two layoutColumns
100
- var columnOne = layoutColumn.createAndFill({}, (0, _marks.removeDisallowedMarks)(currentContainerContent, layoutColumn));
101
- var columnTwo = layoutColumn.createAndFill();
102
- if (!columnOne || !columnTwo) {
103
- currentContainerContent = [];
104
- return;
105
- }
106
- var layout = layoutSection.createAndFill({}, [columnOne, columnTwo]);
107
- if (layout) {
108
- result.push(layout);
109
- hasCreatedContainer = true;
110
- }
111
- currentContainerContent = [];
112
- return;
136
+ container = createLayoutSection(currentContainerContent, layoutSection, layoutColumn);
137
+ } else if (isCodeblock) {
138
+ container = createTextContentContainer(currentContainerContent, targetNodeType, schema);
139
+ } else {
140
+ container = createNodeContentContainer(currentContainerContent, targetNodeType);
113
141
  }
114
-
115
- // For regular containers, create directly
116
- var containerNode = targetNodeType.createAndFill({}, currentContainerContent);
117
- if (containerNode) {
118
- result.push(containerNode);
142
+ if (container) {
143
+ result.push(container);
119
144
  hasCreatedContainer = true;
120
145
  }
121
146
  currentContainerContent = [];
122
147
  };
123
- var processNode = function processNode(node) {
148
+ var canNodeBeWrapped = function canNodeBeWrapped(node) {
124
149
  var validationType = isLayout ? layoutColumn : targetNodeType;
125
- var canWrapNode = validationType.validContent(_model.Fragment.from((0, _marks.removeDisallowedMarks)([node], validationType)));
126
-
127
- // Node can be wrapped - add to current container content
128
- if (canWrapNode) {
129
- var _currentContainerCont;
130
- // remove marks from node as nested nodes don't usually support block marks
131
- (_currentContainerCont = currentContainerContent).push.apply(_currentContainerCont, (0, _toConsumableArray2.default)((0, _marks.removeDisallowedMarks)([node], validationType)));
150
+ return validationType.validContent(_model.Fragment.from((0, _marks.removeDisallowedMarks)([node], validationType)));
151
+ };
152
+ var handleWrappableNode = function handleWrappableNode(node) {
153
+ var _currentContainerCont;
154
+ var validationType = isLayout ? layoutColumn : targetNodeType;
155
+ (_currentContainerCont = currentContainerContent).push.apply(_currentContainerCont, (0, _toConsumableArray2.default)((0, _marks.removeDisallowedMarks)([node], validationType)));
156
+ };
157
+ var handleCodeblockTextNode = function handleCodeblockTextNode(node) {
158
+ currentContainerContent.push((0, _utils.createTextContent)(node));
159
+ };
160
+ var handleConvertibleTextNode = function handleConvertibleTextNode(node) {
161
+ var paragraph = (0, _utils.convertTextNodeToParagraph)(node, schema);
162
+ if (paragraph) {
163
+ currentContainerContent.push(paragraph);
164
+ }
165
+ };
166
+ var handleUnsupportedNode = function handleUnsupportedNode(node) {
167
+ flushCurrentContainer();
168
+ result.push(node);
169
+ };
170
+ var processNode = function processNode(node) {
171
+ if (canNodeBeWrapped(node)) {
172
+ handleWrappableNode(node);
173
+ return;
174
+ }
175
+ if ((0, _utils.isTextNode)(node) && isCodeblock) {
176
+ handleCodeblockTextNode(node);
132
177
  return;
133
178
  }
134
-
135
- // Text node (heading, paragraph) that can't be wrapped - convert to paragraph
136
- // Example: heading can't go in blockquote, so convert to paragraph with same content
137
179
  if ((0, _utils.isTextNode)(node)) {
138
- var paragraph = (0, _utils.convertTextNodeToParagraph)(node, schema);
139
- if (paragraph) {
140
- currentContainerContent.push(paragraph);
141
- }
180
+ handleConvertibleTextNode(node);
142
181
  return;
143
182
  }
144
183
 
145
184
  // All other nodes that cannot be wrapped in the target node - break out
146
185
  // Examples: same-type containers, tables in panels, layoutSections in layouts
147
- flushCurrentContainer();
148
- result.push(node);
186
+ handleUnsupportedNode(node);
149
187
  };
150
188
  nodes.forEach(processNode);
151
-
152
- // Flush any remaining content into a container
153
189
  flushCurrentContainer();
154
-
155
- // Skip edge case handling for layouts since layouts always have columns
156
190
  if (isLayout) {
157
191
  return result.length > 0 ? result : nodes;
158
192
  }
@@ -17,7 +17,6 @@ var _unwrapLayoutStep = require("./steps/unwrapLayoutStep");
17
17
  var _unwrapListStep = require("./steps/unwrapListStep");
18
18
  var _wrapBlockquoteToDecisionListStep = require("./steps/wrapBlockquoteToDecisionListStep");
19
19
  var _wrapMixedContentStep = require("./steps/wrapMixedContentStep");
20
- var _wrapTextToCodeblock = require("./steps/wrapTextToCodeblock");
21
20
  var _types = require("./types");
22
21
  var _unwrapExpandStep = require("./unwrapExpandStep");
23
22
  var _unwrapStep = require("./unwrapStep");
@@ -36,7 +35,7 @@ var TRANSFORM_STEPS = {
36
35
  atomic: undefined,
37
36
  container: [_unwrapStep.unwrapStep, _wrapStep.wrapStep],
38
37
  list: undefined,
39
- text: [_unwrapStep.unwrapStep, _applyTargetTextTypeStep.applyTargetTextTypeStep],
38
+ text: [_unwrapStep.unwrapStep],
40
39
  multi: undefined
41
40
  },
42
41
  list: {
@@ -67,18 +66,13 @@ var TRANSFORM_STEPS = {
67
66
  // Use 'null' to indicate unavailable transfrorm for a case where TRANSFORM_STEPS are not undefined.
68
67
  var TRANSFORM_STEPS_OVERRIDE = {
69
68
  paragraph: {
70
- paragraph: null,
71
- codeBlock: [_wrapTextToCodeblock.wrapTextToCodeblockStep],
72
- layoutSection: [_wrapMixedContentStep.wrapMixedContentStep]
73
- },
74
- heading: {
75
- codeBlock: [_wrapTextToCodeblock.wrapTextToCodeblockStep],
76
- layoutSection: [_wrapMixedContentStep.wrapMixedContentStep]
69
+ paragraph: null
77
70
  },
71
+ heading: {},
78
72
  panel: {
79
73
  panel: null,
80
74
  layoutSection: [_unwrapStep.unwrapStep, _wrapMixedContentStep.wrapMixedContentStep],
81
- codeBlock: [_unwrapStep.unwrapStep, _flattenStep.flattenStep, _wrapStep.wrapStep],
75
+ codeBlock: [_unwrapStep.unwrapStep, _wrapMixedContentStep.wrapMixedContentStep],
82
76
  blockquote: [_unwrapStep.unwrapStep, _wrapMixedContentStep.wrapMixedContentStep],
83
77
  taskList: null,
84
78
  bulletList: null,
@@ -109,7 +103,9 @@ var TRANSFORM_STEPS_OVERRIDE = {
109
103
  nestedExpand: [_wrapStep.wrapStep],
110
104
  layoutSection: [_wrapMixedContentStep.wrapMixedContentStep],
111
105
  codeBlock: null,
112
- decisionList: [_unwrapStep.unwrapStep, _wrapBlockquoteToDecisionListStep.wrapBlockquoteToDecisionListStep]
106
+ decisionList: [_unwrapStep.unwrapStep, _wrapBlockquoteToDecisionListStep.wrapBlockquoteToDecisionListStep],
107
+ paragraph: [_unwrapStep.unwrapStep],
108
+ heading: [_unwrapStep.unwrapStep, _applyTargetTextTypeStep.applyTargetTextTypeStep]
113
109
  },
114
110
  layoutSection: {
115
111
  layoutSection: null,
@@ -127,6 +123,7 @@ var TRANSFORM_STEPS_OVERRIDE = {
127
123
  nestedExpand: [_wrapStep.wrapStep],
128
124
  layoutSection: [_wrapMixedContentStep.wrapMixedContentStep],
129
125
  panel: [_wrapStep.wrapStep],
126
+ paragraph: [_applyTargetTextTypeStep.applyTargetTextTypeStep],
130
127
  heading: null
131
128
  },
132
129
  bulletList: {
@@ -221,10 +218,11 @@ var TRANSFORM_STEPS_OVERRIDE = {
221
218
  decisionList: null
222
219
  },
223
220
  multi: {
224
- // TODO: EDITOR-4140 - Implement multiple paragraphs/headings/codeblocks to heading transform
225
- heading: null,
226
- // TODO: EDITOR-4141 - Implement multiple codeblocks/headings to paragraph transform
227
- paragraph: null
221
+ heading: [_applyTargetTextTypeStep.applyTargetTextTypeStep]
222
+ // Similar to heading, all structures are kept as is
223
+ // EG: transformed: other lists, paragarph, headings
224
+ // eg: not-transformed: quotes, codeblocks ... all typeof 'containers'
225
+ // decisionList: [],
228
226
  }
229
227
  };
230
228
  var getTransformStepsForNodeTypes = function getTransformStepsForNodeTypes(selectedNodeTypeName, targetNodeTypeName) {
@@ -51,8 +51,14 @@ var transformNode = exports.transformNode = function transformNode(api) {
51
51
  });
52
52
  var content = resultNodes.length > 0 ? resultNodes : slice.content;
53
53
  if (preservedSelection instanceof _state.NodeSelection && preservedSelection.node.type === nodes.mediaSingle) {
54
- tr.deleteRange($from.pos, $to.pos);
55
- tr.insert($from.pos, content);
54
+ // when node is media single, use tr.replaceWith freeze editor, if modify position, tr.replaceWith creates duplicats
55
+ var deleteFrom = $from.pos;
56
+ var deleteTo = $to.pos;
57
+ tr.delete(deleteFrom, deleteTo);
58
+ // After deletion, recalculate the insertion position to ensure it's valid
59
+ // especially when mediaSingle with caption is at the bottom of the document
60
+ var insertPos = Math.min(deleteFrom, tr.doc.content.size);
61
+ tr.insert(insertPos, content);
56
62
  } else {
57
63
  tr.replaceWith(sliceStart, $to.pos, content);
58
64
  }
@@ -10,14 +10,6 @@ export const flattenStep = (nodes, context) => {
10
10
  if (!targetNodeType || !paragraph) {
11
11
  return nodes;
12
12
  }
13
-
14
- // TODO: EDITOR-2920 - Implement flattening logic.
15
- const isTargetCodeBlock = targetNodeTypeName === 'codeBlock';
16
- if (isTargetCodeBlock) {
17
- // This strips explicitly text nodes
18
- const codeBlockContent = nodes.map(node => node.content.textBetween(0, node.content.size, '\n')).join('\n');
19
- return [schema.nodes.codeBlock.create({}, schema.text(codeBlockContent))];
20
- }
21
13
  return nodes.map(node => {
22
14
  const isValidWithin = targetNodeType.validContent(node.content);
23
15
  if (!isValidWithin) {
@@ -1,16 +1,19 @@
1
1
  /**
2
- * Applies target text type conversion. If the target type is a heading, converts textblock nodes
3
- * (paragraphs, headings) to heading nodes with the specified level. Otherwise, leaves nodes unchanged.
4
- * Non-textblock nodes are always left unchanged.
2
+ * Applies target text type conversion. Converts textblock nodes to the target text type
3
+ * (paragraph or heading). Non-textblock nodes are left unchanged.
5
4
  *
6
5
  * @example
7
6
  * Input:
8
- * - paragraph "Heading 1"
9
- * - paragraph "Heading 2"
7
+ * - paragraph "Text 1"
8
+ * - paragraph "Text 2"
10
9
  *
11
10
  * Output (with target: heading, level: 2):
12
- * - heading (level: 2) "Heading 1"
13
- * - heading (level: 2) "Heading 2"
11
+ * - heading (level: 2) "Text 1"
12
+ * - heading (level: 2) "Text 2"
13
+ *
14
+ * Output (with target: paragraph):
15
+ * - paragraph "Text 1"
16
+ * - paragraph "Text 2"
14
17
  *
15
18
  * @param nodes
16
19
  * @param context
@@ -22,23 +25,24 @@ export const applyTargetTextTypeStep = (nodes, context) => {
22
25
  targetNodeTypeName,
23
26
  targetAttrs
24
27
  } = context;
25
- if (targetNodeTypeName !== 'heading') {
28
+ if (targetNodeTypeName !== 'heading' && targetNodeTypeName !== 'paragraph') {
26
29
  return nodes;
27
30
  }
28
- const headingType = schema.nodes.heading;
29
- if (!headingType) {
31
+ const targetType = schema.nodes[targetNodeTypeName];
32
+ if (!targetType) {
30
33
  return nodes;
31
34
  }
32
-
33
- // Default to level 1 if no level is specified
34
- // The level should ideally come from targetAttrs, but if not available, use default
35
- const headingLevel = typeof (targetAttrs === null || targetAttrs === void 0 ? void 0 : targetAttrs.level) === 'number' ? targetAttrs.level : 1;
36
35
  return nodes.map(node => {
37
36
  if (node.isTextblock) {
38
- // Convert textblock nodes (paragraphs, headings) to heading with specified level
39
- return headingType.create({
40
- level: headingLevel
41
- }, node.content, node.marks);
37
+ // Convert textblock nodes to the target type with content preserved
38
+ let attrs = {};
39
+ if (targetNodeTypeName === 'heading') {
40
+ const level = typeof (targetAttrs === null || targetAttrs === void 0 ? void 0 : targetAttrs.level) === 'number' ? targetAttrs.level : 1;
41
+ attrs = {
42
+ level
43
+ };
44
+ }
45
+ return targetType.create(attrs, node.content, node.marks);
42
46
  }
43
47
  // Non-textblock nodes are left unchanged
44
48
  return node;
@@ -1,7 +1,35 @@
1
1
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
2
  import { removeDisallowedMarks } from '../marks';
3
3
  import { NODE_CATEGORY_BY_TYPE } from '../types';
4
- import { convertTextNodeToParagraph, isTextNode } from '../utils';
4
+ import { convertTextNodeToParagraph, createTextContent, isTextNode } from '../utils';
5
+
6
+ /**
7
+ * Creates a layout section with two columns, where the first column contains the provided content.
8
+ */
9
+ const createLayoutSection = (content, layoutSection, layoutColumn) => {
10
+ const columnOne = layoutColumn.createAndFill({}, removeDisallowedMarks(content, layoutColumn));
11
+ const columnTwo = layoutColumn.createAndFill();
12
+ if (!columnOne || !columnTwo) {
13
+ return null;
14
+ }
15
+ return layoutSection.createAndFill({}, [columnOne, columnTwo]);
16
+ };
17
+
18
+ /**
19
+ * Creates a container with text content (for codeblocks).
20
+ */
21
+ const createTextContentContainer = (textContentArray, targetNodeType, schema) => {
22
+ const textContent = textContentArray.join('\n');
23
+ const textNode = textContent ? schema.text(textContent) : null;
24
+ return targetNodeType.createAndFill({}, textNode);
25
+ };
26
+
27
+ /**
28
+ * Creates a regular container with node content.
29
+ */
30
+ const createNodeContentContainer = (nodeContent, targetNodeType) => {
31
+ return targetNodeType.createAndFill({}, nodeContent);
32
+ };
5
33
 
6
34
  /**
7
35
  * Handles the edge case where transforming from a container to another container results in
@@ -27,12 +55,16 @@ const handleEmptyContainerEdgeCase = (result, hasCreatedContainer, fromNode, tar
27
55
  // (meaning there were no valid children that could be wrapped)
28
56
  const allContentBrokeOut = !hasCreatedContainer && result.length > 0;
29
57
  const shouldCreateEmptyTarget = isFromContainer && isTargetContainer && allContentBrokeOut;
30
- if (shouldCreateEmptyTarget) {
31
- const emptyParagraph = schema.nodes.paragraph.create();
32
- const emptyContainer = targetNodeType.create({}, emptyParagraph);
33
- return [emptyContainer, ...result];
58
+ if (!shouldCreateEmptyTarget) {
59
+ return result;
60
+ }
61
+ if (targetNodeTypeName === schema.nodes.codeBlock.name) {
62
+ const emptyCodeBlock = createTextContentContainer([], schema.nodes.codeBlock, schema);
63
+ return emptyCodeBlock ? [emptyCodeBlock, ...result] : result;
34
64
  }
35
- return result;
65
+ const emptyParagraph = schema.nodes.paragraph.create();
66
+ const emptyContainer = targetNodeType.create({}, emptyParagraph);
67
+ return [emptyContainer, ...result];
36
68
  };
37
69
 
38
70
  /**
@@ -50,6 +82,9 @@ const handleEmptyContainerEdgeCase = (result, hasCreatedContainer, fromNode, tar
50
82
  * - Layouts always require layoutColumns as children (never paragraphs directly)
51
83
  * - Layout columns can contain most block content including headings, paragraphs, lists, etc.
52
84
  *
85
+ * Special handling for codeblocks:
86
+ * - Text nodes are converted to plain text and added to the codeblock
87
+ *
53
88
  * Edge case handling:
54
89
  * - For regular containers: If all content breaks out (container → container transform with no
55
90
  * valid children), an empty container with a paragraph is created to ensure the target type exists
@@ -80,6 +115,7 @@ export const wrapMixedContentStep = (nodes, context) => {
80
115
  return nodes;
81
116
  }
82
117
  const isLayout = targetNodeTypeName === 'layoutSection';
118
+ const isCodeblock = targetNodeTypeName === 'codeBlock';
83
119
  const {
84
120
  layoutSection,
85
121
  layoutColumn
@@ -91,63 +127,61 @@ export const wrapMixedContentStep = (nodes, context) => {
91
127
  if (currentContainerContent.length === 0) {
92
128
  return;
93
129
  }
130
+ let container = null;
94
131
  if (isLayout) {
95
- // For layouts, create layoutSection with two layoutColumns
96
- const columnOne = layoutColumn.createAndFill({}, removeDisallowedMarks(currentContainerContent, layoutColumn));
97
- const columnTwo = layoutColumn.createAndFill();
98
- if (!columnOne || !columnTwo) {
99
- currentContainerContent = [];
100
- return;
101
- }
102
- const layout = layoutSection.createAndFill({}, [columnOne, columnTwo]);
103
- if (layout) {
104
- result.push(layout);
105
- hasCreatedContainer = true;
106
- }
107
- currentContainerContent = [];
108
- return;
132
+ container = createLayoutSection(currentContainerContent, layoutSection, layoutColumn);
133
+ } else if (isCodeblock) {
134
+ container = createTextContentContainer(currentContainerContent, targetNodeType, schema);
135
+ } else {
136
+ container = createNodeContentContainer(currentContainerContent, targetNodeType);
109
137
  }
110
-
111
- // For regular containers, create directly
112
- const containerNode = targetNodeType.createAndFill({}, currentContainerContent);
113
- if (containerNode) {
114
- result.push(containerNode);
138
+ if (container) {
139
+ result.push(container);
115
140
  hasCreatedContainer = true;
116
141
  }
117
142
  currentContainerContent = [];
118
143
  };
119
- const processNode = node => {
144
+ const canNodeBeWrapped = node => {
120
145
  const validationType = isLayout ? layoutColumn : targetNodeType;
121
- const canWrapNode = validationType.validContent(Fragment.from(removeDisallowedMarks([node], validationType)));
122
-
123
- // Node can be wrapped - add to current container content
124
- if (canWrapNode) {
125
- // remove marks from node as nested nodes don't usually support block marks
126
- currentContainerContent.push(...removeDisallowedMarks([node], validationType));
146
+ return validationType.validContent(Fragment.from(removeDisallowedMarks([node], validationType)));
147
+ };
148
+ const handleWrappableNode = node => {
149
+ const validationType = isLayout ? layoutColumn : targetNodeType;
150
+ currentContainerContent.push(...removeDisallowedMarks([node], validationType));
151
+ };
152
+ const handleCodeblockTextNode = node => {
153
+ currentContainerContent.push(createTextContent(node));
154
+ };
155
+ const handleConvertibleTextNode = node => {
156
+ const paragraph = convertTextNodeToParagraph(node, schema);
157
+ if (paragraph) {
158
+ currentContainerContent.push(paragraph);
159
+ }
160
+ };
161
+ const handleUnsupportedNode = node => {
162
+ flushCurrentContainer();
163
+ result.push(node);
164
+ };
165
+ const processNode = node => {
166
+ if (canNodeBeWrapped(node)) {
167
+ handleWrappableNode(node);
168
+ return;
169
+ }
170
+ if (isTextNode(node) && isCodeblock) {
171
+ handleCodeblockTextNode(node);
127
172
  return;
128
173
  }
129
-
130
- // Text node (heading, paragraph) that can't be wrapped - convert to paragraph
131
- // Example: heading can't go in blockquote, so convert to paragraph with same content
132
174
  if (isTextNode(node)) {
133
- const paragraph = convertTextNodeToParagraph(node, schema);
134
- if (paragraph) {
135
- currentContainerContent.push(paragraph);
136
- }
175
+ handleConvertibleTextNode(node);
137
176
  return;
138
177
  }
139
178
 
140
179
  // All other nodes that cannot be wrapped in the target node - break out
141
180
  // Examples: same-type containers, tables in panels, layoutSections in layouts
142
- flushCurrentContainer();
143
- result.push(node);
181
+ handleUnsupportedNode(node);
144
182
  };
145
183
  nodes.forEach(processNode);
146
-
147
- // Flush any remaining content into a container
148
184
  flushCurrentContainer();
149
-
150
- // Skip edge case handling for layouts since layouts always have columns
151
185
  if (isLayout) {
152
186
  return result.length > 0 ? result : nodes;
153
187
  }
@@ -11,7 +11,6 @@ import { unwrapLayoutStep } from './steps/unwrapLayoutStep';
11
11
  import { unwrapListStep } from './steps/unwrapListStep';
12
12
  import { wrapBlockquoteToDecisionListStep } from './steps/wrapBlockquoteToDecisionListStep';
13
13
  import { wrapMixedContentStep } from './steps/wrapMixedContentStep';
14
- import { wrapTextToCodeblockStep } from './steps/wrapTextToCodeblock';
15
14
  import { getNodeName, NODE_CATEGORY_BY_TYPE, toNodeTypeValue } from './types';
16
15
  import { unwrapExpandStep } from './unwrapExpandStep';
17
16
  import { unwrapStep } from './unwrapStep';
@@ -31,7 +30,7 @@ const TRANSFORM_STEPS = {
31
30
  atomic: undefined,
32
31
  container: [unwrapStep, wrapStep],
33
32
  list: undefined,
34
- text: [unwrapStep, applyTargetTextTypeStep],
33
+ text: [unwrapStep],
35
34
  multi: undefined
36
35
  },
37
36
  list: {
@@ -62,18 +61,13 @@ const TRANSFORM_STEPS = {
62
61
  // Use 'null' to indicate unavailable transfrorm for a case where TRANSFORM_STEPS are not undefined.
63
62
  const TRANSFORM_STEPS_OVERRIDE = {
64
63
  paragraph: {
65
- paragraph: null,
66
- codeBlock: [wrapTextToCodeblockStep],
67
- layoutSection: [wrapMixedContentStep]
68
- },
69
- heading: {
70
- codeBlock: [wrapTextToCodeblockStep],
71
- layoutSection: [wrapMixedContentStep]
64
+ paragraph: null
72
65
  },
66
+ heading: {},
73
67
  panel: {
74
68
  panel: null,
75
69
  layoutSection: [unwrapStep, wrapMixedContentStep],
76
- codeBlock: [unwrapStep, flattenStep, wrapStep],
70
+ codeBlock: [unwrapStep, wrapMixedContentStep],
77
71
  blockquote: [unwrapStep, wrapMixedContentStep],
78
72
  taskList: null,
79
73
  bulletList: null,
@@ -104,7 +98,9 @@ const TRANSFORM_STEPS_OVERRIDE = {
104
98
  nestedExpand: [wrapStep],
105
99
  layoutSection: [wrapMixedContentStep],
106
100
  codeBlock: null,
107
- decisionList: [unwrapStep, wrapBlockquoteToDecisionListStep]
101
+ decisionList: [unwrapStep, wrapBlockquoteToDecisionListStep],
102
+ paragraph: [unwrapStep],
103
+ heading: [unwrapStep, applyTargetTextTypeStep]
108
104
  },
109
105
  layoutSection: {
110
106
  layoutSection: null,
@@ -122,6 +118,7 @@ const TRANSFORM_STEPS_OVERRIDE = {
122
118
  nestedExpand: [wrapStep],
123
119
  layoutSection: [wrapMixedContentStep],
124
120
  panel: [wrapStep],
121
+ paragraph: [applyTargetTextTypeStep],
125
122
  heading: null
126
123
  },
127
124
  bulletList: {
@@ -216,10 +213,11 @@ const TRANSFORM_STEPS_OVERRIDE = {
216
213
  decisionList: null
217
214
  },
218
215
  multi: {
219
- // TODO: EDITOR-4140 - Implement multiple paragraphs/headings/codeblocks to heading transform
220
- heading: null,
221
- // TODO: EDITOR-4141 - Implement multiple codeblocks/headings to paragraph transform
222
- paragraph: null
216
+ heading: [applyTargetTextTypeStep]
217
+ // Similar to heading, all structures are kept as is
218
+ // EG: transformed: other lists, paragarph, headings
219
+ // eg: not-transformed: quotes, codeblocks ... all typeof 'containers'
220
+ // decisionList: [],
223
221
  }
224
222
  };
225
223
  const getTransformStepsForNodeTypes = (selectedNodeTypeName, targetNodeTypeName) => {
@@ -45,8 +45,14 @@ export const transformNode = api => (targetType, metadata) => ({
45
45
  });
46
46
  const content = resultNodes.length > 0 ? resultNodes : slice.content;
47
47
  if (preservedSelection instanceof NodeSelection && preservedSelection.node.type === nodes.mediaSingle) {
48
- tr.deleteRange($from.pos, $to.pos);
49
- tr.insert($from.pos, content);
48
+ // when node is media single, use tr.replaceWith freeze editor, if modify position, tr.replaceWith creates duplicats
49
+ const deleteFrom = $from.pos;
50
+ const deleteTo = $to.pos;
51
+ tr.delete(deleteFrom, deleteTo);
52
+ // After deletion, recalculate the insertion position to ensure it's valid
53
+ // especially when mediaSingle with caption is at the bottom of the document
54
+ const insertPos = Math.min(deleteFrom, tr.doc.content.size);
55
+ tr.insert(insertPos, content);
50
56
  } else {
51
57
  tr.replaceWith(sliceStart, $to.pos, content);
52
58
  }
@@ -6,16 +6,6 @@ export var flattenStep = function flattenStep(nodes, context) {
6
6
  if (!targetNodeType || !paragraph) {
7
7
  return nodes;
8
8
  }
9
-
10
- // TODO: EDITOR-2920 - Implement flattening logic.
11
- var isTargetCodeBlock = targetNodeTypeName === 'codeBlock';
12
- if (isTargetCodeBlock) {
13
- // This strips explicitly text nodes
14
- var codeBlockContent = nodes.map(function (node) {
15
- return node.content.textBetween(0, node.content.size, '\n');
16
- }).join('\n');
17
- return [schema.nodes.codeBlock.create({}, schema.text(codeBlockContent))];
18
- }
19
9
  return nodes.map(function (node) {
20
10
  var isValidWithin = targetNodeType.validContent(node.content);
21
11
  if (!isValidWithin) {
@@ -1,16 +1,19 @@
1
1
  /**
2
- * Applies target text type conversion. If the target type is a heading, converts textblock nodes
3
- * (paragraphs, headings) to heading nodes with the specified level. Otherwise, leaves nodes unchanged.
4
- * Non-textblock nodes are always left unchanged.
2
+ * Applies target text type conversion. Converts textblock nodes to the target text type
3
+ * (paragraph or heading). Non-textblock nodes are left unchanged.
5
4
  *
6
5
  * @example
7
6
  * Input:
8
- * - paragraph "Heading 1"
9
- * - paragraph "Heading 2"
7
+ * - paragraph "Text 1"
8
+ * - paragraph "Text 2"
10
9
  *
11
10
  * Output (with target: heading, level: 2):
12
- * - heading (level: 2) "Heading 1"
13
- * - heading (level: 2) "Heading 2"
11
+ * - heading (level: 2) "Text 1"
12
+ * - heading (level: 2) "Text 2"
13
+ *
14
+ * Output (with target: paragraph):
15
+ * - paragraph "Text 1"
16
+ * - paragraph "Text 2"
14
17
  *
15
18
  * @param nodes
16
19
  * @param context
@@ -20,23 +23,24 @@ export var applyTargetTextTypeStep = function applyTargetTextTypeStep(nodes, con
20
23
  var schema = context.schema,
21
24
  targetNodeTypeName = context.targetNodeTypeName,
22
25
  targetAttrs = context.targetAttrs;
23
- if (targetNodeTypeName !== 'heading') {
26
+ if (targetNodeTypeName !== 'heading' && targetNodeTypeName !== 'paragraph') {
24
27
  return nodes;
25
28
  }
26
- var headingType = schema.nodes.heading;
27
- if (!headingType) {
29
+ var targetType = schema.nodes[targetNodeTypeName];
30
+ if (!targetType) {
28
31
  return nodes;
29
32
  }
30
-
31
- // Default to level 1 if no level is specified
32
- // The level should ideally come from targetAttrs, but if not available, use default
33
- var headingLevel = typeof (targetAttrs === null || targetAttrs === void 0 ? void 0 : targetAttrs.level) === 'number' ? targetAttrs.level : 1;
34
33
  return nodes.map(function (node) {
35
34
  if (node.isTextblock) {
36
- // Convert textblock nodes (paragraphs, headings) to heading with specified level
37
- return headingType.create({
38
- level: headingLevel
39
- }, node.content, node.marks);
35
+ // Convert textblock nodes to the target type with content preserved
36
+ var attrs = {};
37
+ if (targetNodeTypeName === 'heading') {
38
+ var level = typeof (targetAttrs === null || targetAttrs === void 0 ? void 0 : targetAttrs.level) === 'number' ? targetAttrs.level : 1;
39
+ attrs = {
40
+ level: level
41
+ };
42
+ }
43
+ return targetType.create(attrs, node.content, node.marks);
40
44
  }
41
45
  // Non-textblock nodes are left unchanged
42
46
  return node;
@@ -2,7 +2,35 @@ 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, isTextNode } from '../utils';
5
+ import { convertTextNodeToParagraph, createTextContent, isTextNode } from '../utils';
6
+
7
+ /**
8
+ * Creates a layout section with two columns, where the first column contains the provided content.
9
+ */
10
+ var createLayoutSection = function createLayoutSection(content, layoutSection, layoutColumn) {
11
+ var columnOne = layoutColumn.createAndFill({}, removeDisallowedMarks(content, layoutColumn));
12
+ var columnTwo = layoutColumn.createAndFill();
13
+ if (!columnOne || !columnTwo) {
14
+ return null;
15
+ }
16
+ return layoutSection.createAndFill({}, [columnOne, columnTwo]);
17
+ };
18
+
19
+ /**
20
+ * Creates a container with text content (for codeblocks).
21
+ */
22
+ var createTextContentContainer = function createTextContentContainer(textContentArray, targetNodeType, schema) {
23
+ var textContent = textContentArray.join('\n');
24
+ var textNode = textContent ? schema.text(textContent) : null;
25
+ return targetNodeType.createAndFill({}, textNode);
26
+ };
27
+
28
+ /**
29
+ * Creates a regular container with node content.
30
+ */
31
+ var createNodeContentContainer = function createNodeContentContainer(nodeContent, targetNodeType) {
32
+ return targetNodeType.createAndFill({}, nodeContent);
33
+ };
6
34
 
7
35
  /**
8
36
  * Handles the edge case where transforming from a container to another container results in
@@ -28,12 +56,16 @@ var handleEmptyContainerEdgeCase = function handleEmptyContainerEdgeCase(result,
28
56
  // (meaning there were no valid children that could be wrapped)
29
57
  var allContentBrokeOut = !hasCreatedContainer && result.length > 0;
30
58
  var shouldCreateEmptyTarget = isFromContainer && isTargetContainer && allContentBrokeOut;
31
- if (shouldCreateEmptyTarget) {
32
- var emptyParagraph = schema.nodes.paragraph.create();
33
- var emptyContainer = targetNodeType.create({}, emptyParagraph);
34
- return [emptyContainer].concat(_toConsumableArray(result));
59
+ if (!shouldCreateEmptyTarget) {
60
+ return result;
61
+ }
62
+ if (targetNodeTypeName === schema.nodes.codeBlock.name) {
63
+ var emptyCodeBlock = createTextContentContainer([], schema.nodes.codeBlock, schema);
64
+ return emptyCodeBlock ? [emptyCodeBlock].concat(_toConsumableArray(result)) : result;
35
65
  }
36
- return result;
66
+ var emptyParagraph = schema.nodes.paragraph.create();
67
+ var emptyContainer = targetNodeType.create({}, emptyParagraph);
68
+ return [emptyContainer].concat(_toConsumableArray(result));
37
69
  };
38
70
 
39
71
  /**
@@ -51,6 +83,9 @@ var handleEmptyContainerEdgeCase = function handleEmptyContainerEdgeCase(result,
51
83
  * - Layouts always require layoutColumns as children (never paragraphs directly)
52
84
  * - Layout columns can contain most block content including headings, paragraphs, lists, etc.
53
85
  *
86
+ * Special handling for codeblocks:
87
+ * - Text nodes are converted to plain text and added to the codeblock
88
+ *
54
89
  * Edge case handling:
55
90
  * - For regular containers: If all content breaks out (container → container transform with no
56
91
  * valid children), an empty container with a paragraph is created to ensure the target type exists
@@ -79,6 +114,7 @@ export var wrapMixedContentStep = function wrapMixedContentStep(nodes, context)
79
114
  return nodes;
80
115
  }
81
116
  var isLayout = targetNodeTypeName === 'layoutSection';
117
+ var isCodeblock = targetNodeTypeName === 'codeBlock';
82
118
  var _schema$nodes = schema.nodes,
83
119
  layoutSection = _schema$nodes.layoutSection,
84
120
  layoutColumn = _schema$nodes.layoutColumn;
@@ -89,64 +125,62 @@ export var wrapMixedContentStep = function wrapMixedContentStep(nodes, context)
89
125
  if (currentContainerContent.length === 0) {
90
126
  return;
91
127
  }
128
+ var container = null;
92
129
  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);
103
- hasCreatedContainer = true;
104
- }
105
- currentContainerContent = [];
106
- return;
130
+ container = createLayoutSection(currentContainerContent, layoutSection, layoutColumn);
131
+ } else if (isCodeblock) {
132
+ container = createTextContentContainer(currentContainerContent, targetNodeType, schema);
133
+ } else {
134
+ container = createNodeContentContainer(currentContainerContent, targetNodeType);
107
135
  }
108
-
109
- // For regular containers, create directly
110
- var containerNode = targetNodeType.createAndFill({}, currentContainerContent);
111
- if (containerNode) {
112
- result.push(containerNode);
136
+ if (container) {
137
+ result.push(container);
113
138
  hasCreatedContainer = true;
114
139
  }
115
140
  currentContainerContent = [];
116
141
  };
117
- var processNode = function processNode(node) {
142
+ var canNodeBeWrapped = function canNodeBeWrapped(node) {
118
143
  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) {
123
- var _currentContainerCont;
124
- // remove marks from node as nested nodes don't usually support block marks
125
- (_currentContainerCont = currentContainerContent).push.apply(_currentContainerCont, _toConsumableArray(removeDisallowedMarks([node], validationType)));
144
+ return validationType.validContent(Fragment.from(removeDisallowedMarks([node], validationType)));
145
+ };
146
+ var handleWrappableNode = function handleWrappableNode(node) {
147
+ var _currentContainerCont;
148
+ var validationType = isLayout ? layoutColumn : targetNodeType;
149
+ (_currentContainerCont = currentContainerContent).push.apply(_currentContainerCont, _toConsumableArray(removeDisallowedMarks([node], validationType)));
150
+ };
151
+ var handleCodeblockTextNode = function handleCodeblockTextNode(node) {
152
+ currentContainerContent.push(createTextContent(node));
153
+ };
154
+ var handleConvertibleTextNode = function handleConvertibleTextNode(node) {
155
+ var paragraph = convertTextNodeToParagraph(node, schema);
156
+ if (paragraph) {
157
+ currentContainerContent.push(paragraph);
158
+ }
159
+ };
160
+ var handleUnsupportedNode = function handleUnsupportedNode(node) {
161
+ flushCurrentContainer();
162
+ result.push(node);
163
+ };
164
+ var processNode = function processNode(node) {
165
+ if (canNodeBeWrapped(node)) {
166
+ handleWrappableNode(node);
167
+ return;
168
+ }
169
+ if (isTextNode(node) && isCodeblock) {
170
+ handleCodeblockTextNode(node);
126
171
  return;
127
172
  }
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
173
  if (isTextNode(node)) {
132
- var paragraph = convertTextNodeToParagraph(node, schema);
133
- if (paragraph) {
134
- currentContainerContent.push(paragraph);
135
- }
174
+ handleConvertibleTextNode(node);
136
175
  return;
137
176
  }
138
177
 
139
178
  // All other nodes that cannot be wrapped in the target node - break out
140
179
  // Examples: same-type containers, tables in panels, layoutSections in layouts
141
- flushCurrentContainer();
142
- result.push(node);
180
+ handleUnsupportedNode(node);
143
181
  };
144
182
  nodes.forEach(processNode);
145
-
146
- // Flush any remaining content into a container
147
183
  flushCurrentContainer();
148
-
149
- // Skip edge case handling for layouts since layouts always have columns
150
184
  if (isLayout) {
151
185
  return result.length > 0 ? result : nodes;
152
186
  }
@@ -11,7 +11,6 @@ import { unwrapLayoutStep } from './steps/unwrapLayoutStep';
11
11
  import { unwrapListStep } from './steps/unwrapListStep';
12
12
  import { wrapBlockquoteToDecisionListStep } from './steps/wrapBlockquoteToDecisionListStep';
13
13
  import { wrapMixedContentStep } from './steps/wrapMixedContentStep';
14
- import { wrapTextToCodeblockStep } from './steps/wrapTextToCodeblock';
15
14
  import { getNodeName, NODE_CATEGORY_BY_TYPE, toNodeTypeValue } from './types';
16
15
  import { unwrapExpandStep } from './unwrapExpandStep';
17
16
  import { unwrapStep } from './unwrapStep';
@@ -31,7 +30,7 @@ var TRANSFORM_STEPS = {
31
30
  atomic: undefined,
32
31
  container: [unwrapStep, wrapStep],
33
32
  list: undefined,
34
- text: [unwrapStep, applyTargetTextTypeStep],
33
+ text: [unwrapStep],
35
34
  multi: undefined
36
35
  },
37
36
  list: {
@@ -62,18 +61,13 @@ var TRANSFORM_STEPS = {
62
61
  // Use 'null' to indicate unavailable transfrorm for a case where TRANSFORM_STEPS are not undefined.
63
62
  var TRANSFORM_STEPS_OVERRIDE = {
64
63
  paragraph: {
65
- paragraph: null,
66
- codeBlock: [wrapTextToCodeblockStep],
67
- layoutSection: [wrapMixedContentStep]
68
- },
69
- heading: {
70
- codeBlock: [wrapTextToCodeblockStep],
71
- layoutSection: [wrapMixedContentStep]
64
+ paragraph: null
72
65
  },
66
+ heading: {},
73
67
  panel: {
74
68
  panel: null,
75
69
  layoutSection: [unwrapStep, wrapMixedContentStep],
76
- codeBlock: [unwrapStep, flattenStep, wrapStep],
70
+ codeBlock: [unwrapStep, wrapMixedContentStep],
77
71
  blockquote: [unwrapStep, wrapMixedContentStep],
78
72
  taskList: null,
79
73
  bulletList: null,
@@ -104,7 +98,9 @@ var TRANSFORM_STEPS_OVERRIDE = {
104
98
  nestedExpand: [wrapStep],
105
99
  layoutSection: [wrapMixedContentStep],
106
100
  codeBlock: null,
107
- decisionList: [unwrapStep, wrapBlockquoteToDecisionListStep]
101
+ decisionList: [unwrapStep, wrapBlockquoteToDecisionListStep],
102
+ paragraph: [unwrapStep],
103
+ heading: [unwrapStep, applyTargetTextTypeStep]
108
104
  },
109
105
  layoutSection: {
110
106
  layoutSection: null,
@@ -122,6 +118,7 @@ var TRANSFORM_STEPS_OVERRIDE = {
122
118
  nestedExpand: [wrapStep],
123
119
  layoutSection: [wrapMixedContentStep],
124
120
  panel: [wrapStep],
121
+ paragraph: [applyTargetTextTypeStep],
125
122
  heading: null
126
123
  },
127
124
  bulletList: {
@@ -216,10 +213,11 @@ var TRANSFORM_STEPS_OVERRIDE = {
216
213
  decisionList: null
217
214
  },
218
215
  multi: {
219
- // TODO: EDITOR-4140 - Implement multiple paragraphs/headings/codeblocks to heading transform
220
- heading: null,
221
- // TODO: EDITOR-4141 - Implement multiple codeblocks/headings to paragraph transform
222
- paragraph: null
216
+ heading: [applyTargetTextTypeStep]
217
+ // Similar to heading, all structures are kept as is
218
+ // EG: transformed: other lists, paragarph, headings
219
+ // eg: not-transformed: quotes, codeblocks ... all typeof 'containers'
220
+ // decisionList: [],
223
221
  }
224
222
  };
225
223
  var getTransformStepsForNodeTypes = function getTransformStepsForNodeTypes(selectedNodeTypeName, targetNodeTypeName) {
@@ -44,8 +44,14 @@ export var transformNode = function transformNode(api) {
44
44
  });
45
45
  var content = resultNodes.length > 0 ? resultNodes : slice.content;
46
46
  if (preservedSelection instanceof NodeSelection && preservedSelection.node.type === nodes.mediaSingle) {
47
- tr.deleteRange($from.pos, $to.pos);
48
- tr.insert($from.pos, content);
47
+ // when node is media single, use tr.replaceWith freeze editor, if modify position, tr.replaceWith creates duplicats
48
+ var deleteFrom = $from.pos;
49
+ var deleteTo = $to.pos;
50
+ tr.delete(deleteFrom, deleteTo);
51
+ // After deletion, recalculate the insertion position to ensure it's valid
52
+ // especially when mediaSingle with caption is at the bottom of the document
53
+ var insertPos = Math.min(deleteFrom, tr.doc.content.size);
54
+ tr.insert(insertPos, content);
49
55
  } else {
50
56
  tr.replaceWith(sliceStart, $to.pos, content);
51
57
  }
@@ -1,17 +1,20 @@
1
1
  import type { TransformStep } from '../types';
2
2
  /**
3
- * Applies target text type conversion. If the target type is a heading, converts textblock nodes
4
- * (paragraphs, headings) to heading nodes with the specified level. Otherwise, leaves nodes unchanged.
5
- * Non-textblock nodes are always left unchanged.
3
+ * Applies target text type conversion. Converts textblock nodes to the target text type
4
+ * (paragraph or heading). Non-textblock nodes are left unchanged.
6
5
  *
7
6
  * @example
8
7
  * Input:
9
- * - paragraph "Heading 1"
10
- * - paragraph "Heading 2"
8
+ * - paragraph "Text 1"
9
+ * - paragraph "Text 2"
11
10
  *
12
11
  * Output (with target: heading, level: 2):
13
- * - heading (level: 2) "Heading 1"
14
- * - heading (level: 2) "Heading 2"
12
+ * - heading (level: 2) "Text 1"
13
+ * - heading (level: 2) "Text 2"
14
+ *
15
+ * Output (with target: paragraph):
16
+ * - paragraph "Text 1"
17
+ * - paragraph "Text 2"
15
18
  *
16
19
  * @param nodes
17
20
  * @param context
@@ -14,6 +14,9 @@ import type { TransformStep } from '../types';
14
14
  * - Layouts always require layoutColumns as children (never paragraphs directly)
15
15
  * - Layout columns can contain most block content including headings, paragraphs, lists, etc.
16
16
  *
17
+ * Special handling for codeblocks:
18
+ * - Text nodes are converted to plain text and added to the codeblock
19
+ *
17
20
  * Edge case handling:
18
21
  * - For regular containers: If all content breaks out (container → container transform with no
19
22
  * valid children), an empty container with a paragraph is created to ensure the target type exists
@@ -1,17 +1,20 @@
1
1
  import type { TransformStep } from '../types';
2
2
  /**
3
- * Applies target text type conversion. If the target type is a heading, converts textblock nodes
4
- * (paragraphs, headings) to heading nodes with the specified level. Otherwise, leaves nodes unchanged.
5
- * Non-textblock nodes are always left unchanged.
3
+ * Applies target text type conversion. Converts textblock nodes to the target text type
4
+ * (paragraph or heading). Non-textblock nodes are left unchanged.
6
5
  *
7
6
  * @example
8
7
  * Input:
9
- * - paragraph "Heading 1"
10
- * - paragraph "Heading 2"
8
+ * - paragraph "Text 1"
9
+ * - paragraph "Text 2"
11
10
  *
12
11
  * Output (with target: heading, level: 2):
13
- * - heading (level: 2) "Heading 1"
14
- * - heading (level: 2) "Heading 2"
12
+ * - heading (level: 2) "Text 1"
13
+ * - heading (level: 2) "Text 2"
14
+ *
15
+ * Output (with target: paragraph):
16
+ * - paragraph "Text 1"
17
+ * - paragraph "Text 2"
15
18
  *
16
19
  * @param nodes
17
20
  * @param context
@@ -14,6 +14,9 @@ import type { TransformStep } from '../types';
14
14
  * - Layouts always require layoutColumns as children (never paragraphs directly)
15
15
  * - Layout columns can contain most block content including headings, paragraphs, lists, etc.
16
16
  *
17
+ * Special handling for codeblocks:
18
+ * - Text nodes are converted to plain text and added to the codeblock
19
+ *
17
20
  * Edge case handling:
18
21
  * - For regular containers: If all content breaks out (container → container transform with no
19
22
  * valid children), an empty container with a paragraph is created to ensure the target type exists
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-block-menu",
3
- "version": "6.0.7",
3
+ "version": "6.0.8",
4
4
  "description": "BlockMenu plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -45,7 +45,7 @@
45
45
  "@atlaskit/platform-feature-flags-react": "^0.4.0",
46
46
  "@atlaskit/primitives": "^17.0.0",
47
47
  "@atlaskit/tmp-editor-statsig": "^16.4.0",
48
- "@atlaskit/tokens": "^9.0.0",
48
+ "@atlaskit/tokens": "^9.1.0",
49
49
  "@babel/runtime": "^7.0.0"
50
50
  },
51
51
  "peerDependencies": {
@@ -1,21 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.wrapTextToCodeblockStep = void 0;
7
- var _utils = require("../utils");
8
- /**
9
- * Transforms a paragraph (or heading) into a codeBlock by extracting its text content.
10
- * This step handles the conversion of inline content (including marks) to plain text,
11
- * which is required because codeBlocks can only contain plain text nodes.
12
- *
13
- * Example: paragraph with bold/italic/status → codeBlock with plain text
14
- */
15
- var wrapTextToCodeblockStep = exports.wrapTextToCodeblockStep = function wrapTextToCodeblockStep(nodes, context) {
16
- var schema = context.schema;
17
- return nodes.map(function (node) {
18
- var codeBlockNode = schema.nodes.codeBlock.createAndFill({}, schema.text((0, _utils.createTextContent)(node)));
19
- return codeBlockNode !== null && codeBlockNode !== void 0 ? codeBlockNode : node;
20
- });
21
- };
@@ -1,18 +0,0 @@
1
- import { createTextContent } from '../utils';
2
-
3
- /**
4
- * Transforms a paragraph (or heading) into a codeBlock by extracting its text content.
5
- * This step handles the conversion of inline content (including marks) to plain text,
6
- * which is required because codeBlocks can only contain plain text nodes.
7
- *
8
- * Example: paragraph with bold/italic/status → codeBlock with plain text
9
- */
10
- export const wrapTextToCodeblockStep = (nodes, context) => {
11
- const {
12
- schema
13
- } = context;
14
- return nodes.map(node => {
15
- const codeBlockNode = schema.nodes.codeBlock.createAndFill({}, schema.text(createTextContent(node)));
16
- return codeBlockNode !== null && codeBlockNode !== void 0 ? codeBlockNode : node;
17
- });
18
- };
@@ -1,16 +0,0 @@
1
- import { createTextContent } from '../utils';
2
-
3
- /**
4
- * Transforms a paragraph (or heading) into a codeBlock by extracting its text content.
5
- * This step handles the conversion of inline content (including marks) to plain text,
6
- * which is required because codeBlocks can only contain plain text nodes.
7
- *
8
- * Example: paragraph with bold/italic/status → codeBlock with plain text
9
- */
10
- export var wrapTextToCodeblockStep = function wrapTextToCodeblockStep(nodes, context) {
11
- var schema = context.schema;
12
- return nodes.map(function (node) {
13
- var codeBlockNode = schema.nodes.codeBlock.createAndFill({}, schema.text(createTextContent(node)));
14
- return codeBlockNode !== null && codeBlockNode !== void 0 ? codeBlockNode : node;
15
- });
16
- };
@@ -1,9 +0,0 @@
1
- import type { TransformStep } from '../types';
2
- /**
3
- * Transforms a paragraph (or heading) into a codeBlock by extracting its text content.
4
- * This step handles the conversion of inline content (including marks) to plain text,
5
- * which is required because codeBlocks can only contain plain text nodes.
6
- *
7
- * Example: paragraph with bold/italic/status → codeBlock with plain text
8
- */
9
- export declare const wrapTextToCodeblockStep: TransformStep;
@@ -1,9 +0,0 @@
1
- import type { TransformStep } from '../types';
2
- /**
3
- * Transforms a paragraph (or heading) into a codeBlock by extracting its text content.
4
- * This step handles the conversion of inline content (including marks) to plain text,
5
- * which is required because codeBlocks can only contain plain text nodes.
6
- *
7
- * Example: paragraph with bold/italic/status → codeBlock with plain text
8
- */
9
- export declare const wrapTextToCodeblockStep: TransformStep;