@atlaskit/editor-plugin-block-menu 1.0.10 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @atlaskit/editor-plugin-block-menu
2
2
 
3
+ ## 1.0.12
4
+
5
+ ### Patch Changes
6
+
7
+ - [`6e27819feadc7`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/6e27819feadc7) -
8
+ ED-29183: Fixed aligned p and heading not able to convert to list
9
+ - [`6a9265f5389db`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/6a9265f5389db) -
10
+ Fix container transform with blockquote containing list
11
+ - [`24e0d952943aa`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/24e0d952943aa) -
12
+ ED-29206: Fixed conversion from code block to panel, expand text formatting issue
13
+ - Updated dependencies
14
+
15
+ ## 1.0.11
16
+
17
+ ### Patch Changes
18
+
19
+ - [`fcef7ff2e1083`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/fcef7ff2e1083) -
20
+ Split unsupported content when converting to codeblock
21
+ - [`1754f5027f568`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/1754f5027f568) -
22
+ Fix missing copy link on table node
23
+ - Updated dependencies
24
+
3
25
  ## 1.0.10
4
26
 
5
27
  ### Patch Changes
@@ -20,19 +20,6 @@ var convertInvalidNodeToValidNodeType = function convertInvalidNodeToValidNodeTy
20
20
  });
21
21
  return _model.Fragment.from(validTransformedContent);
22
22
  };
23
- var filterMarksForTargetNodeType = function filterMarksForTargetNodeType(content, targetNodeType) {
24
- var withValidMarks = [];
25
- content.forEach(function (node) {
26
- if (node.marks.length > 0) {
27
- var allowedMarks = targetNodeType.allowedMarks(node.marks);
28
- var updatedNode = node.mark(allowedMarks);
29
- withValidMarks.push(updatedNode);
30
- } else {
31
- withValidMarks.push(node);
32
- }
33
- });
34
- return _model.Fragment.from(withValidMarks);
35
- };
36
23
 
37
24
  /**
38
25
  * Transform selection to container type
@@ -47,7 +34,8 @@ var transformToContainer = exports.transformToContainer = function transformToCo
47
34
  var content = selection.content().content;
48
35
  var transformedContent = content;
49
36
  if (sourceNode.type === schema.nodes.codeBlock) {
50
- transformedContent = convertInvalidNodeToValidNodeType(transformedContent, schema.nodes.codeBlock, schema.nodes.paragraph);
37
+ var paragraphNodes = (0, _utils.convertCodeBlockContentToParagraphs)(sourceNode, schema);
38
+ transformedContent = _model.Fragment.fromArray(paragraphNodes);
51
39
  }
52
40
  if (targetNodeType === schema.nodes.blockquote) {
53
41
  transformedContent = convertInvalidNodeToValidNodeType(transformedContent, schema.nodes.heading, schema.nodes.paragraph, true);
@@ -57,7 +45,7 @@ var transformToContainer = exports.transformToContainer = function transformToCo
57
45
  // e.g. blocks (heading/ paragraph) with alignment need to remove alignment
58
46
  // as panel/ blockQuote/ expands does not support alignment
59
47
  if (sourceNode.type === schema.nodes.paragraph || sourceNode.type === schema.nodes.heading) {
60
- transformedContent = filterMarksForTargetNodeType(transformedContent, targetNodeType);
48
+ transformedContent = (0, _utils.filterMarksForTargetNodeType)(transformedContent, targetNodeType);
61
49
  }
62
50
  var newNode = targetNodeType.createAndFill(targetAttrs, transformedContent);
63
51
  if (!newNode) {
@@ -82,6 +70,16 @@ var transformContainerNode = exports.transformContainerNode = function transform
82
70
 
83
71
  // Transform container to block type - unwrap and convert content
84
72
  if ((0, _utils.isBlockNodeType)(targetNodeType)) {
73
+ // special case container to codeblock
74
+ if (targetNodeType.name === 'codeBlock') {
75
+ return transformBetweenContainerTypes({
76
+ tr: tr,
77
+ sourceNode: sourceNode,
78
+ sourcePos: sourcePos,
79
+ targetNodeType: targetNodeType,
80
+ targetAttrs: targetAttrs
81
+ });
82
+ }
85
83
  return unwrapAndConvertToBlockType({
86
84
  tr: tr,
87
85
  sourceNode: sourceNode,
@@ -148,12 +146,7 @@ var unwrapAndConvertToBlockType = exports.unwrapAndConvertToBlockType = function
148
146
 
149
147
  // if the container is a code block, convert text content to multiple paragraphs
150
148
  if (sourceNode.type === codeBlock) {
151
- var codeText = sourceNode.textContent;
152
- var lines = codeText.split('\n');
153
- var paragraphNodes = lines.map(function (line) {
154
- return paragraph.create(null, line ? schema.text(line) : null);
155
- });
156
- sourceChildren = paragraphNodes;
149
+ sourceChildren = (0, _utils.convertCodeBlockContentToParagraphs)(sourceNode, schema);
157
150
  }
158
151
 
159
152
  // if target node is a paragraph, just do unwrap
@@ -274,18 +267,29 @@ var transformBetweenContainerTypes = exports.transformBetweenContainerTypes = fu
274
267
  targetNodeType = context.targetNodeType,
275
268
  targetAttrs = context.targetAttrs;
276
269
 
270
+ // Special handling for codeBlock target
271
+ if (targetNodeType.name === 'codeBlock') {
272
+ var _contentSplits = splitContentForCodeBlock(sourceNode, targetNodeType, targetAttrs, tr.doc.type.schema);
273
+ return applySplitsToTransaction(tr, sourcePos, sourceNode.nodeSize, _contentSplits);
274
+ }
275
+
277
276
  // Get content validation for target container type
278
277
  var isContentSupported = (0, _utils.getContentSupportChecker)(targetNodeType);
279
278
 
280
279
  // Process content and collect splits
281
280
  var contentSplits = splitContentAroundUnsupportedBlocks(sourceNode, isContentSupported, targetNodeType, targetAttrs, tr.doc.type.schema);
281
+ return applySplitsToTransaction(tr, sourcePos, sourceNode.nodeSize, contentSplits);
282
+ };
282
283
 
283
- // Replace the original node with the first split
284
+ /**
285
+ * Apply content splits to transaction - shared utility for replacing and inserting splits
286
+ */
287
+ var applySplitsToTransaction = function applySplitsToTransaction(tr, sourcePos, sourceNodeSize, contentSplits) {
284
288
  var insertPos = sourcePos;
285
289
  contentSplits.forEach(function (splitNode, index) {
286
290
  if (index === 0) {
287
291
  // Replace the original node with the first split
288
- tr.replaceWith(sourcePos, sourcePos + sourceNode.nodeSize, splitNode);
292
+ tr.replaceWith(sourcePos, sourcePos + sourceNodeSize, splitNode);
289
293
  insertPos = sourcePos + splitNode.nodeSize;
290
294
  } else {
291
295
  // Insert additional splits after
@@ -296,18 +300,75 @@ var transformBetweenContainerTypes = exports.transformBetweenContainerTypes = fu
296
300
  return tr;
297
301
  };
298
302
 
303
+ /**
304
+ * Split content for codeBlock transformation, creating codeBlocks for text content
305
+ * and preserving unsupported blocks (like tables) separately
306
+ */
307
+ var splitContentForCodeBlock = function splitContentForCodeBlock(sourceNode, targetNodeType, targetAttrs, schema) {
308
+ var _sourceNode$attrs3;
309
+ var splits = [];
310
+ var children = sourceNode.content.content;
311
+ var currentTextContent = [];
312
+
313
+ // Handle expand title - add as first text if source is expand with title
314
+ if (sourceNode.type.name === 'expand' && (_sourceNode$attrs3 = sourceNode.attrs) !== null && _sourceNode$attrs3 !== void 0 && _sourceNode$attrs3.title) {
315
+ currentTextContent.push(sourceNode.attrs.title);
316
+ }
317
+ var flushCurrentCodeBlock = function flushCurrentCodeBlock() {
318
+ if (currentTextContent.length > 0) {
319
+ var codeText = currentTextContent.join('\n');
320
+ var codeBlockNode = targetNodeType.create(targetAttrs, schema.text(codeText));
321
+ splits.push(codeBlockNode);
322
+ currentTextContent = [];
323
+ }
324
+ };
325
+ var isCodeBlockCompatible = function isCodeBlockCompatible(node) {
326
+ // Only text blocks (paragraph, heading) can be converted to codeBlock text
327
+ return node.isTextblock || node.type.name === 'codeBlock';
328
+ };
329
+ children.forEach(function (childNode) {
330
+ if (isCodeBlockCompatible(childNode)) {
331
+ // Extract text content from compatible nodes
332
+ if (childNode.type.name === 'codeBlock') {
333
+ // If it's already a codeBlock, extract its text
334
+ currentTextContent.push(childNode.textContent);
335
+ } else if (childNode.isTextblock) {
336
+ // Extract text from text blocks (paragraphs, headings, etc.)
337
+ var text = childNode.textContent;
338
+ if (text.trim()) {
339
+ currentTextContent.push(text);
340
+ }
341
+ }
342
+ } else if ((0, _utils.isBlockNodeForExtraction)(childNode)) {
343
+ // Unsupported block node (table, etc.) - flush current codeBlock, add block, continue
344
+ flushCurrentCodeBlock();
345
+ splits.push(childNode);
346
+ } else {
347
+ // Other unsupported content - try to extract text if possible
348
+ var _text = childNode.textContent;
349
+ if (_text && _text.trim()) {
350
+ currentTextContent.push(_text);
351
+ }
352
+ }
353
+ });
354
+
355
+ // Flush any remaining text content as a codeBlock
356
+ flushCurrentCodeBlock();
357
+ return splits;
358
+ };
359
+
299
360
  /**
300
361
  * Split content around unsupported block nodes, creating separate containers
301
362
  * for content before and after each unsupported block
302
363
  */
303
364
  var splitContentAroundUnsupportedBlocks = function splitContentAroundUnsupportedBlocks(sourceNode, isContentSupported, targetNodeType, targetAttrs, schema) {
304
- var _sourceNode$attrs3;
365
+ var _sourceNode$attrs4;
305
366
  var splits = [];
306
367
  var children = sourceNode.content.content;
307
368
  var currentContainerContent = [];
308
369
 
309
370
  // Handle expand title - add as first paragraph if source is expand with title
310
- if (sourceNode.type.name === 'expand' && (_sourceNode$attrs3 = sourceNode.attrs) !== null && _sourceNode$attrs3 !== void 0 && _sourceNode$attrs3.title) {
371
+ if (sourceNode.type.name === 'expand' && (_sourceNode$attrs4 = sourceNode.attrs) !== null && _sourceNode$attrs4 !== void 0 && _sourceNode$attrs4.title) {
311
372
  var titleParagraph = schema.nodes.paragraph.create({}, schema.text(sourceNode.attrs.title));
312
373
  currentContainerContent.push(titleParagraph);
313
374
  }
@@ -322,15 +383,15 @@ var splitContentAroundUnsupportedBlocks = function splitContentAroundUnsupported
322
383
  if (isContentSupported(childNode)) {
323
384
  // Supported content - add to current container
324
385
  currentContainerContent.push(childNode);
325
- } else if ((0, _utils.isBlockNodeForExtraction)(childNode)) {
326
- // Unsupported block node - flush current container, add block, continue
327
- flushCurrentContainer();
328
- splits.push(childNode);
329
386
  } else if (childNode.type.name === targetNodeType.name) {
330
387
  // Same type of container merge contents
331
388
  childNode.content.forEach(function (child) {
332
389
  currentContainerContent.push(child);
333
390
  });
391
+ } else if (childNode.isBlock) {
392
+ // Unsupported block node - flush current container, add block, continue
393
+ flushCurrentContainer();
394
+ splits.push(childNode);
334
395
  } else {
335
396
  // Unsupported inline content - convert to paragraph and add to container
336
397
  var inlineContent = (0, _utils.convertNodeToInlineContent)(childNode, schema);
@@ -24,6 +24,7 @@ var transformBlockToList = exports.transformBlockToList = function transformBloc
24
24
  var _tr$selection = tr.selection,
25
25
  $from = _tr$selection.$from,
26
26
  $to = _tr$selection.$to;
27
+ var schema = tr.doc.type.schema;
27
28
  var range = $from.blockRange($to);
28
29
  if (!range) {
29
30
  return null;
@@ -36,6 +37,12 @@ var transformBlockToList = exports.transformBlockToList = function transformBloc
36
37
  return (0, _transformToTaskList.transformToTaskList)(tr, range, targetNodeType, targetAttrs, nodes);
37
38
  }
38
39
 
40
+ // filter marks that are not allowed in the target node type
41
+ if (sourceNode.type === schema.nodes.paragraph || sourceNode.type === schema.nodes.heading) {
42
+ var allowedMarks = targetNodeType.allowedMarks(sourceNode.marks);
43
+ tr.setNodeMarkup(range.start, null, null, allowedMarks);
44
+ }
45
+
39
46
  // For headings, convert to paragraph first since headings cannot be direct children of list items
40
47
  if (sourceNode && sourceNode.type.name.startsWith('heading')) {
41
48
  tr.setBlockType(range.start, range.end, nodes.paragraph);
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.isTaskList = exports.isListNodeType = exports.isListNode = exports.isLayoutNodeType = exports.isContainerNodeType = exports.isContainerNode = exports.isBulletOrOrderedList = exports.isBlockNodeType = exports.isBlockNodeForExtraction = exports.isBlockNode = exports.getTargetNodeInfo = exports.getSupportedListTypesSet = exports.getSupportedListTypes = exports.getContentSupportChecker = exports.convertNodeToInlineContent = void 0;
6
+ exports.isTaskList = exports.isListNodeType = exports.isListNode = exports.isLayoutNodeType = exports.isContainerNodeType = exports.isContainerNode = exports.isBulletOrOrderedList = exports.isBlockNodeType = exports.isBlockNodeForExtraction = exports.isBlockNode = exports.getTargetNodeInfo = exports.getSupportedListTypesSet = exports.getSupportedListTypes = exports.getContentSupportChecker = exports.filterMarksForTargetNodeType = exports.convertNodeToInlineContent = exports.convertCodeBlockContentToParagraphs = void 0;
7
7
  var _model = require("@atlaskit/editor-prosemirror/model");
8
8
  var getTargetNodeInfo = exports.getTargetNodeInfo = function getTargetNodeInfo(targetType, nodes) {
9
9
  switch (targetType) {
@@ -174,4 +174,37 @@ var convertNodeToInlineContent = exports.convertNodeToInlineContent = function c
174
174
  return [schema.text(node.textContent)];
175
175
  }
176
176
  return inlineNodes;
177
+ };
178
+
179
+ /**
180
+ * Filter marks from content based on the target node type
181
+ * @param content The content fragment to filter
182
+ * @param targetNodeType The target node type to check against
183
+ * @returns A new fragment with marks filtered for the target node type
184
+ */
185
+ var filterMarksForTargetNodeType = exports.filterMarksForTargetNodeType = function filterMarksForTargetNodeType(content, targetNodeType) {
186
+ var withValidMarks = [];
187
+ content.forEach(function (node) {
188
+ if (node.marks.length > 0) {
189
+ var allowedMarks = targetNodeType.allowedMarks(node.marks);
190
+ var updatedNode = node.mark(allowedMarks);
191
+ withValidMarks.push(updatedNode);
192
+ } else {
193
+ withValidMarks.push(node);
194
+ }
195
+ });
196
+ return _model.Fragment.from(withValidMarks);
197
+ };
198
+
199
+ /** * Convert content from a code block node into multiple paragraph nodes
200
+ */
201
+ var convertCodeBlockContentToParagraphs = exports.convertCodeBlockContentToParagraphs = function convertCodeBlockContentToParagraphs(codeBlockNode, schema) {
202
+ var paragraphNodes = [];
203
+ var codeText = codeBlockNode.textContent;
204
+ var lines = codeText.split('\n');
205
+ lines.forEach(function (line) {
206
+ var paragraphNode = schema.nodes.paragraph.create(null, line ? schema.text(line) : null);
207
+ paragraphNodes.push(paragraphNode);
208
+ });
209
+ return paragraphNodes;
177
210
  };
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.isNestedNode = void 0;
7
7
  var _state = require("@atlaskit/editor-prosemirror/state");
8
+ var _editorTables = require("@atlaskit/editor-tables");
8
9
  /**
9
10
  * Determines if a node is nested (not at top-level) based on its depth and context.
10
11
  *
@@ -37,6 +38,11 @@ var isNestedNode = exports.isNestedNode = function isNestedNode(selection) {
37
38
  return true;
38
39
  }
39
40
 
41
+ // Special case for table selection
42
+ if (selection instanceof _editorTables.CellSelection) {
43
+ return depth > 3;
44
+ }
45
+
40
46
  // Check parent node type for depth 2-3
41
47
  var parentNode = $from.node(depth - 1);
42
48
  if (!parentNode) {
@@ -1,5 +1,5 @@
1
1
  import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
2
- import { isBlockNodeType, isListNodeType, isContainerNodeType, isBlockNodeForExtraction, convertNodeToInlineContent, getContentSupportChecker } from './utils';
2
+ import { isBlockNodeType, isListNodeType, isContainerNodeType, isBlockNodeForExtraction, convertNodeToInlineContent, getContentSupportChecker, convertCodeBlockContentToParagraphs, filterMarksForTargetNodeType } from './utils';
3
3
  const convertInvalidNodeToValidNodeType = (sourceContent, sourceNodeType, validNodeType, withMarks) => {
4
4
  const validTransformedContent = [];
5
5
  // Headings are not valid inside headings so convert heading nodes to paragraphs
@@ -12,19 +12,6 @@ const convertInvalidNodeToValidNodeType = (sourceContent, sourceNodeType, validN
12
12
  });
13
13
  return Fragment.from(validTransformedContent);
14
14
  };
15
- const filterMarksForTargetNodeType = (content, targetNodeType) => {
16
- const withValidMarks = [];
17
- content.forEach(node => {
18
- if (node.marks.length > 0) {
19
- const allowedMarks = targetNodeType.allowedMarks(node.marks);
20
- const updatedNode = node.mark(allowedMarks);
21
- withValidMarks.push(updatedNode);
22
- } else {
23
- withValidMarks.push(node);
24
- }
25
- });
26
- return Fragment.from(withValidMarks);
27
- };
28
15
 
29
16
  /**
30
17
  * Transform selection to container type
@@ -40,7 +27,8 @@ export const transformToContainer = ({
40
27
  const content = selection.content().content;
41
28
  let transformedContent = content;
42
29
  if (sourceNode.type === schema.nodes.codeBlock) {
43
- transformedContent = convertInvalidNodeToValidNodeType(transformedContent, schema.nodes.codeBlock, schema.nodes.paragraph);
30
+ const paragraphNodes = convertCodeBlockContentToParagraphs(sourceNode, schema);
31
+ transformedContent = Fragment.fromArray(paragraphNodes);
44
32
  }
45
33
  if (targetNodeType === schema.nodes.blockquote) {
46
34
  transformedContent = convertInvalidNodeToValidNodeType(transformedContent, schema.nodes.heading, schema.nodes.paragraph, true);
@@ -76,6 +64,16 @@ export const transformContainerNode = ({
76
64
 
77
65
  // Transform container to block type - unwrap and convert content
78
66
  if (isBlockNodeType(targetNodeType)) {
67
+ // special case container to codeblock
68
+ if (targetNodeType.name === 'codeBlock') {
69
+ return transformBetweenContainerTypes({
70
+ tr,
71
+ sourceNode,
72
+ sourcePos,
73
+ targetNodeType,
74
+ targetAttrs
75
+ });
76
+ }
79
77
  return unwrapAndConvertToBlockType({
80
78
  tr,
81
79
  sourceNode,
@@ -147,10 +145,7 @@ export const unwrapAndConvertToBlockType = context => {
147
145
 
148
146
  // if the container is a code block, convert text content to multiple paragraphs
149
147
  if (sourceNode.type === codeBlock) {
150
- const codeText = sourceNode.textContent;
151
- const lines = codeText.split('\n');
152
- const paragraphNodes = lines.map(line => paragraph.create(null, line ? schema.text(line) : null));
153
- sourceChildren = paragraphNodes;
148
+ sourceChildren = convertCodeBlockContentToParagraphs(sourceNode, schema);
154
149
  }
155
150
 
156
151
  // if target node is a paragraph, just do unwrap
@@ -273,18 +268,29 @@ export const transformBetweenContainerTypes = context => {
273
268
  targetAttrs
274
269
  } = context;
275
270
 
271
+ // Special handling for codeBlock target
272
+ if (targetNodeType.name === 'codeBlock') {
273
+ const contentSplits = splitContentForCodeBlock(sourceNode, targetNodeType, targetAttrs, tr.doc.type.schema);
274
+ return applySplitsToTransaction(tr, sourcePos, sourceNode.nodeSize, contentSplits);
275
+ }
276
+
276
277
  // Get content validation for target container type
277
278
  const isContentSupported = getContentSupportChecker(targetNodeType);
278
279
 
279
280
  // Process content and collect splits
280
281
  const contentSplits = splitContentAroundUnsupportedBlocks(sourceNode, isContentSupported, targetNodeType, targetAttrs, tr.doc.type.schema);
282
+ return applySplitsToTransaction(tr, sourcePos, sourceNode.nodeSize, contentSplits);
283
+ };
281
284
 
282
- // Replace the original node with the first split
285
+ /**
286
+ * Apply content splits to transaction - shared utility for replacing and inserting splits
287
+ */
288
+ const applySplitsToTransaction = (tr, sourcePos, sourceNodeSize, contentSplits) => {
283
289
  let insertPos = sourcePos;
284
290
  contentSplits.forEach((splitNode, index) => {
285
291
  if (index === 0) {
286
292
  // Replace the original node with the first split
287
- tr.replaceWith(sourcePos, sourcePos + sourceNode.nodeSize, splitNode);
293
+ tr.replaceWith(sourcePos, sourcePos + sourceNodeSize, splitNode);
288
294
  insertPos = sourcePos + splitNode.nodeSize;
289
295
  } else {
290
296
  // Insert additional splits after
@@ -295,18 +301,75 @@ export const transformBetweenContainerTypes = context => {
295
301
  return tr;
296
302
  };
297
303
 
304
+ /**
305
+ * Split content for codeBlock transformation, creating codeBlocks for text content
306
+ * and preserving unsupported blocks (like tables) separately
307
+ */
308
+ const splitContentForCodeBlock = (sourceNode, targetNodeType, targetAttrs, schema) => {
309
+ var _sourceNode$attrs3;
310
+ const splits = [];
311
+ const children = sourceNode.content.content;
312
+ let currentTextContent = [];
313
+
314
+ // Handle expand title - add as first text if source is expand with title
315
+ if (sourceNode.type.name === 'expand' && (_sourceNode$attrs3 = sourceNode.attrs) !== null && _sourceNode$attrs3 !== void 0 && _sourceNode$attrs3.title) {
316
+ currentTextContent.push(sourceNode.attrs.title);
317
+ }
318
+ const flushCurrentCodeBlock = () => {
319
+ if (currentTextContent.length > 0) {
320
+ const codeText = currentTextContent.join('\n');
321
+ const codeBlockNode = targetNodeType.create(targetAttrs, schema.text(codeText));
322
+ splits.push(codeBlockNode);
323
+ currentTextContent = [];
324
+ }
325
+ };
326
+ const isCodeBlockCompatible = node => {
327
+ // Only text blocks (paragraph, heading) can be converted to codeBlock text
328
+ return node.isTextblock || node.type.name === 'codeBlock';
329
+ };
330
+ children.forEach(childNode => {
331
+ if (isCodeBlockCompatible(childNode)) {
332
+ // Extract text content from compatible nodes
333
+ if (childNode.type.name === 'codeBlock') {
334
+ // If it's already a codeBlock, extract its text
335
+ currentTextContent.push(childNode.textContent);
336
+ } else if (childNode.isTextblock) {
337
+ // Extract text from text blocks (paragraphs, headings, etc.)
338
+ const text = childNode.textContent;
339
+ if (text.trim()) {
340
+ currentTextContent.push(text);
341
+ }
342
+ }
343
+ } else if (isBlockNodeForExtraction(childNode)) {
344
+ // Unsupported block node (table, etc.) - flush current codeBlock, add block, continue
345
+ flushCurrentCodeBlock();
346
+ splits.push(childNode);
347
+ } else {
348
+ // Other unsupported content - try to extract text if possible
349
+ const text = childNode.textContent;
350
+ if (text && text.trim()) {
351
+ currentTextContent.push(text);
352
+ }
353
+ }
354
+ });
355
+
356
+ // Flush any remaining text content as a codeBlock
357
+ flushCurrentCodeBlock();
358
+ return splits;
359
+ };
360
+
298
361
  /**
299
362
  * Split content around unsupported block nodes, creating separate containers
300
363
  * for content before and after each unsupported block
301
364
  */
302
365
  const splitContentAroundUnsupportedBlocks = (sourceNode, isContentSupported, targetNodeType, targetAttrs, schema) => {
303
- var _sourceNode$attrs3;
366
+ var _sourceNode$attrs4;
304
367
  const splits = [];
305
368
  const children = sourceNode.content.content;
306
369
  let currentContainerContent = [];
307
370
 
308
371
  // Handle expand title - add as first paragraph if source is expand with title
309
- if (sourceNode.type.name === 'expand' && (_sourceNode$attrs3 = sourceNode.attrs) !== null && _sourceNode$attrs3 !== void 0 && _sourceNode$attrs3.title) {
372
+ if (sourceNode.type.name === 'expand' && (_sourceNode$attrs4 = sourceNode.attrs) !== null && _sourceNode$attrs4 !== void 0 && _sourceNode$attrs4.title) {
310
373
  const titleParagraph = schema.nodes.paragraph.create({}, schema.text(sourceNode.attrs.title));
311
374
  currentContainerContent.push(titleParagraph);
312
375
  }
@@ -321,15 +384,15 @@ const splitContentAroundUnsupportedBlocks = (sourceNode, isContentSupported, tar
321
384
  if (isContentSupported(childNode)) {
322
385
  // Supported content - add to current container
323
386
  currentContainerContent.push(childNode);
324
- } else if (isBlockNodeForExtraction(childNode)) {
325
- // Unsupported block node - flush current container, add block, continue
326
- flushCurrentContainer();
327
- splits.push(childNode);
328
387
  } else if (childNode.type.name === targetNodeType.name) {
329
388
  // Same type of container merge contents
330
389
  childNode.content.forEach(child => {
331
390
  currentContainerContent.push(child);
332
391
  });
392
+ } else if (childNode.isBlock) {
393
+ // Unsupported block node - flush current container, add block, continue
394
+ flushCurrentContainer();
395
+ splits.push(childNode);
333
396
  } else {
334
397
  // Unsupported inline content - convert to paragraph and add to container
335
398
  const inlineContent = convertNodeToInlineContent(childNode, schema);
@@ -20,6 +20,7 @@ export const transformBlockToList = context => {
20
20
  $from,
21
21
  $to
22
22
  } = tr.selection;
23
+ const schema = tr.doc.type.schema;
23
24
  const range = $from.blockRange($to);
24
25
  if (!range) {
25
26
  return null;
@@ -34,6 +35,12 @@ export const transformBlockToList = context => {
34
35
  return transformToTaskList(tr, range, targetNodeType, targetAttrs, nodes);
35
36
  }
36
37
 
38
+ // filter marks that are not allowed in the target node type
39
+ if (sourceNode.type === schema.nodes.paragraph || sourceNode.type === schema.nodes.heading) {
40
+ const allowedMarks = targetNodeType.allowedMarks(sourceNode.marks);
41
+ tr.setNodeMarkup(range.start, null, null, allowedMarks);
42
+ }
43
+
37
44
  // For headings, convert to paragraph first since headings cannot be direct children of list items
38
45
  if (sourceNode && sourceNode.type.name.startsWith('heading')) {
39
46
  tr.setBlockType(range.start, range.end, nodes.paragraph);
@@ -168,4 +168,37 @@ export const convertNodeToInlineContent = (node, schema) => {
168
168
  return [schema.text(node.textContent)];
169
169
  }
170
170
  return inlineNodes;
171
+ };
172
+
173
+ /**
174
+ * Filter marks from content based on the target node type
175
+ * @param content The content fragment to filter
176
+ * @param targetNodeType The target node type to check against
177
+ * @returns A new fragment with marks filtered for the target node type
178
+ */
179
+ export const filterMarksForTargetNodeType = (content, targetNodeType) => {
180
+ const withValidMarks = [];
181
+ content.forEach(node => {
182
+ if (node.marks.length > 0) {
183
+ const allowedMarks = targetNodeType.allowedMarks(node.marks);
184
+ const updatedNode = node.mark(allowedMarks);
185
+ withValidMarks.push(updatedNode);
186
+ } else {
187
+ withValidMarks.push(node);
188
+ }
189
+ });
190
+ return Fragment.from(withValidMarks);
191
+ };
192
+
193
+ /** * Convert content from a code block node into multiple paragraph nodes
194
+ */
195
+ export const convertCodeBlockContentToParagraphs = (codeBlockNode, schema) => {
196
+ const paragraphNodes = [];
197
+ const codeText = codeBlockNode.textContent;
198
+ const lines = codeText.split('\n');
199
+ lines.forEach(line => {
200
+ const paragraphNode = schema.nodes.paragraph.create(null, line ? schema.text(line) : null);
201
+ paragraphNodes.push(paragraphNode);
202
+ });
203
+ return paragraphNodes;
171
204
  };
@@ -1,4 +1,5 @@
1
1
  import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
2
+ import { CellSelection } from '@atlaskit/editor-tables';
2
3
 
3
4
  /**
4
5
  * Determines if a node is nested (not at top-level) based on its depth and context.
@@ -34,6 +35,11 @@ export const isNestedNode = selection => {
34
35
  return true;
35
36
  }
36
37
 
38
+ // Special case for table selection
39
+ if (selection instanceof CellSelection) {
40
+ return depth > 3;
41
+ }
42
+
37
43
  // Check parent node type for depth 2-3
38
44
  const parentNode = $from.node(depth - 1);
39
45
  if (!parentNode) {
@@ -1,6 +1,6 @@
1
1
  import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
2
  import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
3
- import { isBlockNodeType, isListNodeType, isContainerNodeType, isBlockNodeForExtraction, convertNodeToInlineContent, getContentSupportChecker } from './utils';
3
+ import { isBlockNodeType, isListNodeType, isContainerNodeType, isBlockNodeForExtraction, convertNodeToInlineContent, getContentSupportChecker, convertCodeBlockContentToParagraphs, filterMarksForTargetNodeType } from './utils';
4
4
  var convertInvalidNodeToValidNodeType = function convertInvalidNodeToValidNodeType(sourceContent, sourceNodeType, validNodeType, withMarks) {
5
5
  var validTransformedContent = [];
6
6
  // Headings are not valid inside headings so convert heading nodes to paragraphs
@@ -13,19 +13,6 @@ var convertInvalidNodeToValidNodeType = function convertInvalidNodeToValidNodeTy
13
13
  });
14
14
  return Fragment.from(validTransformedContent);
15
15
  };
16
- var filterMarksForTargetNodeType = function filterMarksForTargetNodeType(content, targetNodeType) {
17
- var withValidMarks = [];
18
- content.forEach(function (node) {
19
- if (node.marks.length > 0) {
20
- var allowedMarks = targetNodeType.allowedMarks(node.marks);
21
- var updatedNode = node.mark(allowedMarks);
22
- withValidMarks.push(updatedNode);
23
- } else {
24
- withValidMarks.push(node);
25
- }
26
- });
27
- return Fragment.from(withValidMarks);
28
- };
29
16
 
30
17
  /**
31
18
  * Transform selection to container type
@@ -40,7 +27,8 @@ export var transformToContainer = function transformToContainer(_ref) {
40
27
  var content = selection.content().content;
41
28
  var transformedContent = content;
42
29
  if (sourceNode.type === schema.nodes.codeBlock) {
43
- transformedContent = convertInvalidNodeToValidNodeType(transformedContent, schema.nodes.codeBlock, schema.nodes.paragraph);
30
+ var paragraphNodes = convertCodeBlockContentToParagraphs(sourceNode, schema);
31
+ transformedContent = Fragment.fromArray(paragraphNodes);
44
32
  }
45
33
  if (targetNodeType === schema.nodes.blockquote) {
46
34
  transformedContent = convertInvalidNodeToValidNodeType(transformedContent, schema.nodes.heading, schema.nodes.paragraph, true);
@@ -75,6 +63,16 @@ export var transformContainerNode = function transformContainerNode(_ref2) {
75
63
 
76
64
  // Transform container to block type - unwrap and convert content
77
65
  if (isBlockNodeType(targetNodeType)) {
66
+ // special case container to codeblock
67
+ if (targetNodeType.name === 'codeBlock') {
68
+ return transformBetweenContainerTypes({
69
+ tr: tr,
70
+ sourceNode: sourceNode,
71
+ sourcePos: sourcePos,
72
+ targetNodeType: targetNodeType,
73
+ targetAttrs: targetAttrs
74
+ });
75
+ }
78
76
  return unwrapAndConvertToBlockType({
79
77
  tr: tr,
80
78
  sourceNode: sourceNode,
@@ -141,12 +139,7 @@ export var unwrapAndConvertToBlockType = function unwrapAndConvertToBlockType(co
141
139
 
142
140
  // if the container is a code block, convert text content to multiple paragraphs
143
141
  if (sourceNode.type === codeBlock) {
144
- var codeText = sourceNode.textContent;
145
- var lines = codeText.split('\n');
146
- var paragraphNodes = lines.map(function (line) {
147
- return paragraph.create(null, line ? schema.text(line) : null);
148
- });
149
- sourceChildren = paragraphNodes;
142
+ sourceChildren = convertCodeBlockContentToParagraphs(sourceNode, schema);
150
143
  }
151
144
 
152
145
  // if target node is a paragraph, just do unwrap
@@ -267,18 +260,29 @@ export var transformBetweenContainerTypes = function transformBetweenContainerTy
267
260
  targetNodeType = context.targetNodeType,
268
261
  targetAttrs = context.targetAttrs;
269
262
 
263
+ // Special handling for codeBlock target
264
+ if (targetNodeType.name === 'codeBlock') {
265
+ var _contentSplits = splitContentForCodeBlock(sourceNode, targetNodeType, targetAttrs, tr.doc.type.schema);
266
+ return applySplitsToTransaction(tr, sourcePos, sourceNode.nodeSize, _contentSplits);
267
+ }
268
+
270
269
  // Get content validation for target container type
271
270
  var isContentSupported = getContentSupportChecker(targetNodeType);
272
271
 
273
272
  // Process content and collect splits
274
273
  var contentSplits = splitContentAroundUnsupportedBlocks(sourceNode, isContentSupported, targetNodeType, targetAttrs, tr.doc.type.schema);
274
+ return applySplitsToTransaction(tr, sourcePos, sourceNode.nodeSize, contentSplits);
275
+ };
275
276
 
276
- // Replace the original node with the first split
277
+ /**
278
+ * Apply content splits to transaction - shared utility for replacing and inserting splits
279
+ */
280
+ var applySplitsToTransaction = function applySplitsToTransaction(tr, sourcePos, sourceNodeSize, contentSplits) {
277
281
  var insertPos = sourcePos;
278
282
  contentSplits.forEach(function (splitNode, index) {
279
283
  if (index === 0) {
280
284
  // Replace the original node with the first split
281
- tr.replaceWith(sourcePos, sourcePos + sourceNode.nodeSize, splitNode);
285
+ tr.replaceWith(sourcePos, sourcePos + sourceNodeSize, splitNode);
282
286
  insertPos = sourcePos + splitNode.nodeSize;
283
287
  } else {
284
288
  // Insert additional splits after
@@ -289,18 +293,75 @@ export var transformBetweenContainerTypes = function transformBetweenContainerTy
289
293
  return tr;
290
294
  };
291
295
 
296
+ /**
297
+ * Split content for codeBlock transformation, creating codeBlocks for text content
298
+ * and preserving unsupported blocks (like tables) separately
299
+ */
300
+ var splitContentForCodeBlock = function splitContentForCodeBlock(sourceNode, targetNodeType, targetAttrs, schema) {
301
+ var _sourceNode$attrs3;
302
+ var splits = [];
303
+ var children = sourceNode.content.content;
304
+ var currentTextContent = [];
305
+
306
+ // Handle expand title - add as first text if source is expand with title
307
+ if (sourceNode.type.name === 'expand' && (_sourceNode$attrs3 = sourceNode.attrs) !== null && _sourceNode$attrs3 !== void 0 && _sourceNode$attrs3.title) {
308
+ currentTextContent.push(sourceNode.attrs.title);
309
+ }
310
+ var flushCurrentCodeBlock = function flushCurrentCodeBlock() {
311
+ if (currentTextContent.length > 0) {
312
+ var codeText = currentTextContent.join('\n');
313
+ var codeBlockNode = targetNodeType.create(targetAttrs, schema.text(codeText));
314
+ splits.push(codeBlockNode);
315
+ currentTextContent = [];
316
+ }
317
+ };
318
+ var isCodeBlockCompatible = function isCodeBlockCompatible(node) {
319
+ // Only text blocks (paragraph, heading) can be converted to codeBlock text
320
+ return node.isTextblock || node.type.name === 'codeBlock';
321
+ };
322
+ children.forEach(function (childNode) {
323
+ if (isCodeBlockCompatible(childNode)) {
324
+ // Extract text content from compatible nodes
325
+ if (childNode.type.name === 'codeBlock') {
326
+ // If it's already a codeBlock, extract its text
327
+ currentTextContent.push(childNode.textContent);
328
+ } else if (childNode.isTextblock) {
329
+ // Extract text from text blocks (paragraphs, headings, etc.)
330
+ var text = childNode.textContent;
331
+ if (text.trim()) {
332
+ currentTextContent.push(text);
333
+ }
334
+ }
335
+ } else if (isBlockNodeForExtraction(childNode)) {
336
+ // Unsupported block node (table, etc.) - flush current codeBlock, add block, continue
337
+ flushCurrentCodeBlock();
338
+ splits.push(childNode);
339
+ } else {
340
+ // Other unsupported content - try to extract text if possible
341
+ var _text = childNode.textContent;
342
+ if (_text && _text.trim()) {
343
+ currentTextContent.push(_text);
344
+ }
345
+ }
346
+ });
347
+
348
+ // Flush any remaining text content as a codeBlock
349
+ flushCurrentCodeBlock();
350
+ return splits;
351
+ };
352
+
292
353
  /**
293
354
  * Split content around unsupported block nodes, creating separate containers
294
355
  * for content before and after each unsupported block
295
356
  */
296
357
  var splitContentAroundUnsupportedBlocks = function splitContentAroundUnsupportedBlocks(sourceNode, isContentSupported, targetNodeType, targetAttrs, schema) {
297
- var _sourceNode$attrs3;
358
+ var _sourceNode$attrs4;
298
359
  var splits = [];
299
360
  var children = sourceNode.content.content;
300
361
  var currentContainerContent = [];
301
362
 
302
363
  // Handle expand title - add as first paragraph if source is expand with title
303
- if (sourceNode.type.name === 'expand' && (_sourceNode$attrs3 = sourceNode.attrs) !== null && _sourceNode$attrs3 !== void 0 && _sourceNode$attrs3.title) {
364
+ if (sourceNode.type.name === 'expand' && (_sourceNode$attrs4 = sourceNode.attrs) !== null && _sourceNode$attrs4 !== void 0 && _sourceNode$attrs4.title) {
304
365
  var titleParagraph = schema.nodes.paragraph.create({}, schema.text(sourceNode.attrs.title));
305
366
  currentContainerContent.push(titleParagraph);
306
367
  }
@@ -315,15 +376,15 @@ var splitContentAroundUnsupportedBlocks = function splitContentAroundUnsupported
315
376
  if (isContentSupported(childNode)) {
316
377
  // Supported content - add to current container
317
378
  currentContainerContent.push(childNode);
318
- } else if (isBlockNodeForExtraction(childNode)) {
319
- // Unsupported block node - flush current container, add block, continue
320
- flushCurrentContainer();
321
- splits.push(childNode);
322
379
  } else if (childNode.type.name === targetNodeType.name) {
323
380
  // Same type of container merge contents
324
381
  childNode.content.forEach(function (child) {
325
382
  currentContainerContent.push(child);
326
383
  });
384
+ } else if (childNode.isBlock) {
385
+ // Unsupported block node - flush current container, add block, continue
386
+ flushCurrentContainer();
387
+ splits.push(childNode);
327
388
  } else {
328
389
  // Unsupported inline content - convert to paragraph and add to container
329
390
  var inlineContent = convertNodeToInlineContent(childNode, schema);
@@ -18,6 +18,7 @@ export var transformBlockToList = function transformBlockToList(context) {
18
18
  var _tr$selection = tr.selection,
19
19
  $from = _tr$selection.$from,
20
20
  $to = _tr$selection.$to;
21
+ var schema = tr.doc.type.schema;
21
22
  var range = $from.blockRange($to);
22
23
  if (!range) {
23
24
  return null;
@@ -30,6 +31,12 @@ export var transformBlockToList = function transformBlockToList(context) {
30
31
  return transformToTaskList(tr, range, targetNodeType, targetAttrs, nodes);
31
32
  }
32
33
 
34
+ // filter marks that are not allowed in the target node type
35
+ if (sourceNode.type === schema.nodes.paragraph || sourceNode.type === schema.nodes.heading) {
36
+ var allowedMarks = targetNodeType.allowedMarks(sourceNode.marks);
37
+ tr.setNodeMarkup(range.start, null, null, allowedMarks);
38
+ }
39
+
33
40
  // For headings, convert to paragraph first since headings cannot be direct children of list items
34
41
  if (sourceNode && sourceNode.type.name.startsWith('heading')) {
35
42
  tr.setBlockType(range.start, range.end, nodes.paragraph);
@@ -168,4 +168,37 @@ export var convertNodeToInlineContent = function convertNodeToInlineContent(node
168
168
  return [schema.text(node.textContent)];
169
169
  }
170
170
  return inlineNodes;
171
+ };
172
+
173
+ /**
174
+ * Filter marks from content based on the target node type
175
+ * @param content The content fragment to filter
176
+ * @param targetNodeType The target node type to check against
177
+ * @returns A new fragment with marks filtered for the target node type
178
+ */
179
+ export var filterMarksForTargetNodeType = function filterMarksForTargetNodeType(content, targetNodeType) {
180
+ var withValidMarks = [];
181
+ content.forEach(function (node) {
182
+ if (node.marks.length > 0) {
183
+ var allowedMarks = targetNodeType.allowedMarks(node.marks);
184
+ var updatedNode = node.mark(allowedMarks);
185
+ withValidMarks.push(updatedNode);
186
+ } else {
187
+ withValidMarks.push(node);
188
+ }
189
+ });
190
+ return Fragment.from(withValidMarks);
191
+ };
192
+
193
+ /** * Convert content from a code block node into multiple paragraph nodes
194
+ */
195
+ export var convertCodeBlockContentToParagraphs = function convertCodeBlockContentToParagraphs(codeBlockNode, schema) {
196
+ var paragraphNodes = [];
197
+ var codeText = codeBlockNode.textContent;
198
+ var lines = codeText.split('\n');
199
+ lines.forEach(function (line) {
200
+ var paragraphNode = schema.nodes.paragraph.create(null, line ? schema.text(line) : null);
201
+ paragraphNodes.push(paragraphNode);
202
+ });
203
+ return paragraphNodes;
171
204
  };
@@ -1,4 +1,5 @@
1
1
  import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
2
+ import { CellSelection } from '@atlaskit/editor-tables';
2
3
 
3
4
  /**
4
5
  * Determines if a node is nested (not at top-level) based on its depth and context.
@@ -32,6 +33,11 @@ export var isNestedNode = function isNestedNode(selection) {
32
33
  return true;
33
34
  }
34
35
 
36
+ // Special case for table selection
37
+ if (selection instanceof CellSelection) {
38
+ return depth > 3;
39
+ }
40
+
35
41
  // Check parent node type for depth 2-3
36
42
  var parentNode = $from.node(depth - 1);
37
43
  if (!parentNode) {
@@ -1,4 +1,5 @@
1
1
  import type { Node as PMNode, NodeType, Schema } from '@atlaskit/editor-prosemirror/model';
2
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
3
  import type { FormatNodeTargetType } from './types';
3
4
  export declare const getTargetNodeInfo: (targetType: FormatNodeTargetType, nodes: Record<string, NodeType>) => {
4
5
  attrs?: Record<string, unknown>;
@@ -28,3 +29,13 @@ export declare const getContentSupportChecker: (targetNodeType: NodeType) => ((n
28
29
  * Convert a node to inline content that can be placed in a paragraph
29
30
  */
30
31
  export declare const convertNodeToInlineContent: (node: PMNode, schema: Schema) => PMNode[];
32
+ /**
33
+ * Filter marks from content based on the target node type
34
+ * @param content The content fragment to filter
35
+ * @param targetNodeType The target node type to check against
36
+ * @returns A new fragment with marks filtered for the target node type
37
+ */
38
+ export declare const filterMarksForTargetNodeType: (content: Fragment, targetNodeType: NodeType) => Fragment;
39
+ /** * Convert content from a code block node into multiple paragraph nodes
40
+ */
41
+ export declare const convertCodeBlockContentToParagraphs: (codeBlockNode: PMNode, schema: Schema) => PMNode[];
@@ -1,4 +1,5 @@
1
1
  import type { Node as PMNode, NodeType, Schema } from '@atlaskit/editor-prosemirror/model';
2
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
3
  import type { FormatNodeTargetType } from './types';
3
4
  export declare const getTargetNodeInfo: (targetType: FormatNodeTargetType, nodes: Record<string, NodeType>) => {
4
5
  attrs?: Record<string, unknown>;
@@ -28,3 +29,13 @@ export declare const getContentSupportChecker: (targetNodeType: NodeType) => ((n
28
29
  * Convert a node to inline content that can be placed in a paragraph
29
30
  */
30
31
  export declare const convertNodeToInlineContent: (node: PMNode, schema: Schema) => PMNode[];
32
+ /**
33
+ * Filter marks from content based on the target node type
34
+ * @param content The content fragment to filter
35
+ * @param targetNodeType The target node type to check against
36
+ * @returns A new fragment with marks filtered for the target node type
37
+ */
38
+ export declare const filterMarksForTargetNodeType: (content: Fragment, targetNodeType: NodeType) => Fragment;
39
+ /** * Convert content from a code block node into multiple paragraph nodes
40
+ */
41
+ export declare const convertCodeBlockContentToParagraphs: (codeBlockNode: PMNode, schema: Schema) => PMNode[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-block-menu",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "BlockMenu plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",