@atlaskit/editor-plugin-block-menu 1.0.0 → 1.0.2

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 (47) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/cjs/editor-commands/formatNode.js +6 -3
  3. package/dist/cjs/editor-commands/transforms/block-transforms.js +22 -10
  4. package/dist/cjs/editor-commands/transforms/container-transforms.js +40 -7
  5. package/dist/cjs/editor-commands/transforms/inline-node-transforms.js +27 -0
  6. package/dist/cjs/editor-commands/transforms/list/transformBetweenListTypes.js +102 -0
  7. package/dist/cjs/editor-commands/transforms/list/transformOrderedUnorderedListToBlockNodes.js +54 -0
  8. package/dist/cjs/editor-commands/transforms/list/transformTaskListToBlockNodes.js +59 -0
  9. package/dist/cjs/editor-commands/transforms/list-transforms.js +66 -20
  10. package/dist/cjs/ui/block-menu-components.js +1 -2
  11. package/dist/cjs/ui/block-menu.compiled.css +1 -1
  12. package/dist/cjs/ui/block-menu.js +1 -1
  13. package/dist/es2019/editor-commands/formatNode.js +6 -3
  14. package/dist/es2019/editor-commands/transforms/block-transforms.js +29 -15
  15. package/dist/es2019/editor-commands/transforms/container-transforms.js +35 -2
  16. package/dist/es2019/editor-commands/transforms/inline-node-transforms.js +21 -0
  17. package/dist/es2019/editor-commands/transforms/list/transformBetweenListTypes.js +98 -0
  18. package/dist/es2019/editor-commands/transforms/list/transformOrderedUnorderedListToBlockNodes.js +42 -0
  19. package/dist/es2019/editor-commands/transforms/list/transformTaskListToBlockNodes.js +47 -0
  20. package/dist/es2019/editor-commands/transforms/list-transforms.js +66 -16
  21. package/dist/es2019/ui/block-menu-components.js +1 -2
  22. package/dist/es2019/ui/block-menu.compiled.css +1 -1
  23. package/dist/es2019/ui/block-menu.js +1 -1
  24. package/dist/esm/editor-commands/formatNode.js +6 -3
  25. package/dist/esm/editor-commands/transforms/block-transforms.js +23 -11
  26. package/dist/esm/editor-commands/transforms/container-transforms.js +39 -7
  27. package/dist/esm/editor-commands/transforms/inline-node-transforms.js +21 -0
  28. package/dist/esm/editor-commands/transforms/list/transformBetweenListTypes.js +95 -0
  29. package/dist/esm/editor-commands/transforms/list/transformOrderedUnorderedListToBlockNodes.js +48 -0
  30. package/dist/esm/editor-commands/transforms/list/transformTaskListToBlockNodes.js +53 -0
  31. package/dist/esm/editor-commands/transforms/list-transforms.js +65 -19
  32. package/dist/esm/ui/block-menu-components.js +1 -2
  33. package/dist/esm/ui/block-menu.compiled.css +1 -1
  34. package/dist/esm/ui/block-menu.js +1 -1
  35. package/dist/types/editor-commands/transforms/container-transforms.d.ts +2 -2
  36. package/dist/types/editor-commands/transforms/inline-node-transforms.d.ts +3 -0
  37. package/dist/types/editor-commands/transforms/list/transformBetweenListTypes.d.ts +9 -0
  38. package/dist/types/editor-commands/transforms/list/transformOrderedUnorderedListToBlockNodes.d.ts +3 -0
  39. package/dist/types/editor-commands/transforms/list/transformTaskListToBlockNodes.d.ts +3 -0
  40. package/dist/types/editor-commands/transforms/list-transforms.d.ts +5 -6
  41. package/dist/types-ts4.5/editor-commands/transforms/container-transforms.d.ts +2 -2
  42. package/dist/types-ts4.5/editor-commands/transforms/inline-node-transforms.d.ts +3 -0
  43. package/dist/types-ts4.5/editor-commands/transforms/list/transformBetweenListTypes.d.ts +9 -0
  44. package/dist/types-ts4.5/editor-commands/transforms/list/transformOrderedUnorderedListToBlockNodes.d.ts +3 -0
  45. package/dist/types-ts4.5/editor-commands/transforms/list/transformTaskListToBlockNodes.d.ts +3 -0
  46. package/dist/types-ts4.5/editor-commands/transforms/list-transforms.d.ts +5 -6
  47. package/package.json +3 -8
@@ -1,5 +1,6 @@
1
1
  import { transformToContainer } from './container-transforms';
2
- import { transformToList } from './list-transforms';
2
+ import { getInlineNodeTextContent } from './inline-node-transforms';
3
+ import { transformBlockToList } from './list-transforms';
3
4
  import { isListNodeType, isContainerNodeType, isBlockNodeType } from './utils';
4
5
 
5
6
  /**
@@ -7,32 +8,45 @@ import { isListNodeType, isContainerNodeType, isBlockNodeType } from './utils';
7
8
  */
8
9
  export const transformBlockNode = context => {
9
10
  const {
10
- tr,
11
- targetNodeType,
12
- targetAttrs
11
+ targetNodeType
13
12
  } = context;
14
- const {
15
- selection
16
- } = tr;
17
- const {
18
- $from,
19
- $to
20
- } = selection;
21
13
 
22
14
  // Handle transformation to list types
23
15
  if (isListNodeType(targetNodeType)) {
24
- return transformToList(context);
16
+ return transformBlockToList(context);
25
17
  }
26
18
 
27
19
  // Handle transformation to container types (panel, expand, blockquote)
28
20
  if (isContainerNodeType(targetNodeType)) {
29
- return transformToContainer();
21
+ return transformToContainer(context);
30
22
  }
31
23
 
32
24
  // Handle block type transformation (paragraph, heading, codeblock)
33
25
  if (isBlockNodeType(targetNodeType)) {
34
- tr.setBlockType($from.pos, $to.pos, targetNodeType, targetAttrs);
35
- return tr;
26
+ return transformToBlockNode(context);
36
27
  }
37
28
  return null;
29
+ };
30
+ const transformToBlockNode = context => {
31
+ const {
32
+ tr,
33
+ targetNodeType,
34
+ targetAttrs
35
+ } = context;
36
+ const {
37
+ selection,
38
+ doc
39
+ } = tr;
40
+ const {
41
+ $from,
42
+ $to
43
+ } = selection;
44
+ const schema = doc.type.schema;
45
+ if (targetNodeType === schema.nodes.codeBlock) {
46
+ const textContent = getInlineNodeTextContent(selection.content().content, tr);
47
+ const node = schema.nodes.codeBlock.createChecked(undefined, textContent);
48
+ return tr.replaceRangeWith(selection.from, selection.to, node);
49
+ }
50
+ tr.setBlockType($from.pos, $to.pos, targetNodeType, targetAttrs);
51
+ return tr;
38
52
  };
@@ -1,10 +1,43 @@
1
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
1
2
  import { isBlockNodeType, isListNodeType, isContainerNodeType } from './utils';
3
+ const convertInvalidNodeToValidNodeType = (sourceContent, sourceNodeType, validNodeType, withMarks) => {
4
+ const validTransformedContent = [];
5
+ // Headings are not valid inside headings so convert heading nodes to paragraphs
6
+ sourceContent.forEach(node => {
7
+ if (sourceNodeType === node.type) {
8
+ validTransformedContent.push(validNodeType.createChecked(node.attrs, node.content, withMarks ? node.marks : undefined));
9
+ } else {
10
+ validTransformedContent.push(node);
11
+ }
12
+ });
13
+ return Fragment.from(validTransformedContent);
14
+ };
2
15
 
3
16
  /**
4
17
  * Transform selection to container type
5
18
  */
6
- export const transformToContainer = () => {
7
- return null;
19
+ export const transformToContainer = ({
20
+ tr,
21
+ sourceNode,
22
+ targetNodeType,
23
+ targetAttrs
24
+ }) => {
25
+ const selection = tr.selection;
26
+ const schema = tr.doc.type.schema;
27
+ const content = selection.content().content;
28
+ let transformedContent = content;
29
+ if (sourceNode.type === schema.nodes.codeBlock) {
30
+ transformedContent = convertInvalidNodeToValidNodeType(transformedContent, schema.nodes.codeBlock, schema.nodes.paragraph);
31
+ }
32
+ if (targetNodeType === schema.nodes.blockquote) {
33
+ transformedContent = convertInvalidNodeToValidNodeType(transformedContent, schema.nodes.heading, schema.nodes.paragraph, true);
34
+ }
35
+ const newNode = targetNodeType.createAndFill(targetAttrs, transformedContent);
36
+ if (!newNode) {
37
+ return null;
38
+ }
39
+ tr.replaceRangeWith(selection.from, selection.to, newNode);
40
+ return tr;
8
41
  };
9
42
 
10
43
  /**
@@ -0,0 +1,21 @@
1
+ export const getInlineNodeTextContent = (sourceContent, tr) => {
2
+ let validTransformedContent = '';
3
+ const schema = tr.doc.type.schema;
4
+ if (sourceContent.content.length > 1) {
5
+ return;
6
+ }
7
+ // Headings are not valid inside headings so convert heading nodes to paragraphs
8
+ sourceContent.forEach(node => {
9
+ if (['paragraph', 'heading'].includes(node.type.name)) {
10
+ node.content.forEach(inlineNode => {
11
+ if (inlineNode.type.name === 'status') {
12
+ validTransformedContent += inlineNode.attrs.text;
13
+ } else {
14
+ validTransformedContent += `${inlineNode.textContent}`;
15
+ }
16
+ });
17
+ validTransformedContent;
18
+ }
19
+ });
20
+ return schema.text(validTransformedContent);
21
+ };
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Extract all inline content from a node
3
+ */
4
+ const extractInlineContent = node => {
5
+ const inlineContent = [];
6
+ for (let i = 0; i < node.childCount; i++) {
7
+ inlineContent.push(node.child(i));
8
+ }
9
+ return inlineContent;
10
+ };
11
+
12
+ /**
13
+ * Transform list structure
14
+ */
15
+ export const transformListStructure = (tr, listNode, targetNodeType, nodes) => {
16
+ try {
17
+ const {
18
+ node: sourceList,
19
+ pos: listPos
20
+ } = listNode;
21
+ const {
22
+ bulletList,
23
+ orderedList,
24
+ taskList,
25
+ listItem,
26
+ taskItem,
27
+ paragraph
28
+ } = nodes;
29
+ const isSourceBulletOrOrdered = sourceList.type === bulletList || sourceList.type === orderedList;
30
+ const isTargetTask = targetNodeType === taskList;
31
+ const isSourceTask = sourceList.type === taskList;
32
+ const newListItems = [];
33
+ const listStart = listPos;
34
+ const listEnd = listPos + sourceList.nodeSize;
35
+
36
+ // Use nodesBetween to efficiently traverse the list structure
37
+ tr.doc.nodesBetween(listStart, listEnd, (node, pos, parent) => {
38
+ // Only process direct children of the list (depth 1)
39
+ if (parent !== sourceList) {
40
+ return true; // Continue traversal
41
+ }
42
+ if (isSourceBulletOrOrdered && isTargetTask) {
43
+ // Converting from bullet/ordered list to task list
44
+ // Extract inline content from all children within listItem
45
+ if (node.type === listItem) {
46
+ const inlineContent = [];
47
+
48
+ // Extract all inline content from all child nodes
49
+ for (let i = 0; i < node.childCount; i++) {
50
+ const child = node.child(i);
51
+ if (child.type === paragraph) {
52
+ // Extract inline content from paragraphs
53
+ inlineContent.push(...extractInlineContent(child));
54
+ } else if (child.isBlock) {
55
+ // For other block content types eg. codeBlock, extract their text content and create text nodes
56
+ const textContent = child.textContent;
57
+ if (textContent) {
58
+ const textNode = tr.doc.type.schema.text(textContent);
59
+ inlineContent.push(textNode);
60
+ }
61
+ } else {
62
+ // Already inline content, add directly
63
+ inlineContent.push(child);
64
+ }
65
+ }
66
+ if (inlineContent.length > 0) {
67
+ const newItem = taskItem.create(null, inlineContent);
68
+ newListItems.push(newItem);
69
+ }
70
+ }
71
+ } else if (isSourceTask && !isTargetTask) {
72
+ // Converting from task list to bullet/ordered list
73
+ // Structure: taskItem > inline content -> listItem > paragraph > inline content
74
+ if (node.type === taskItem) {
75
+ const inlineContent = extractInlineContent(node);
76
+ if (inlineContent.length > 0) {
77
+ const paragraphNode = paragraph.create(null, inlineContent);
78
+ const newListItem = listItem.create(null, paragraphNode);
79
+ newListItems.push(newListItem);
80
+ }
81
+ }
82
+ }
83
+ return false; // Don't traverse into children of list items
84
+ });
85
+ if (newListItems.length === 0) {
86
+ return tr;
87
+ }
88
+
89
+ // Create new list with transformed items
90
+ const newList = targetNodeType.create(null, newListItems);
91
+
92
+ // Replace the entire list
93
+ tr.replaceWith(listStart, listEnd, newList);
94
+ return tr;
95
+ } catch {
96
+ return tr;
97
+ }
98
+ };
@@ -0,0 +1,42 @@
1
+ import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
2
+ import { findChildrenByType } from '@atlaskit/editor-prosemirror/utils';
3
+ export const transformOrderedUnorderedListToBlockNodes = context => {
4
+ const {
5
+ tr,
6
+ targetNodeType,
7
+ targetAttrs,
8
+ sourceNode,
9
+ sourcePos
10
+ } = context;
11
+ const {
12
+ selection
13
+ } = tr;
14
+ const schema = selection.$from.doc.type.schema;
15
+ // find all paragraph nodes inside the list node
16
+ const paragraphs = findChildrenByType(sourceNode, schema.nodes.paragraph);
17
+ const paragraphNodes = paragraphs.map(paragraph => paragraph.node);
18
+ let targetNodes = paragraphNodes;
19
+
20
+ // Convert paragraphs to headings if target is heading
21
+ if (targetNodeType === schema.nodes.heading && targetAttrs) {
22
+ const targetHeadingLevel = targetAttrs.level;
23
+ targetNodes = paragraphNodes.map(paragraphNode => schema.nodes.heading.createChecked({
24
+ level: targetHeadingLevel
25
+ }, paragraphNode.content));
26
+ }
27
+
28
+ // Convert paragraphs to code block if target is code block
29
+ if (targetNodeType === schema.nodes.codeBlock) {
30
+ // convert the paragraphNodes to one code block
31
+ const listItemsResult = findChildrenByType(sourceNode, schema.nodes.listItem);
32
+ const listItems = listItemsResult.map(item => item.node);
33
+ const listItemFragments = listItems.map(listItem => listItem.content);
34
+ const codeBlockContent = listItemFragments.map(fragment => fragment.textBetween(0, fragment.size, '\n')).join('\n');
35
+ targetNodes = [schema.nodes.codeBlock.createChecked({}, schema.text(codeBlockContent))];
36
+ }
37
+ const fragment = Fragment.fromArray(targetNodes);
38
+ const slice = new Slice(fragment, 0, 0);
39
+ const rangeStart = sourcePos !== null ? sourcePos : selection.from;
40
+ tr.replaceRange(rangeStart, rangeStart + sourceNode.nodeSize, slice);
41
+ return tr;
42
+ };
@@ -0,0 +1,47 @@
1
+ import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
2
+ import { findChildrenByType } from '@atlaskit/editor-prosemirror/utils';
3
+ export const transformTaskListToBlockNodes = context => {
4
+ const {
5
+ tr,
6
+ targetNodeType,
7
+ targetAttrs,
8
+ sourceNode,
9
+ sourcePos
10
+ } = context;
11
+ const {
12
+ selection
13
+ } = tr;
14
+ const schema = selection.$from.doc.type.schema;
15
+ const taskItemsResult = findChildrenByType(sourceNode, schema.nodes.taskItem);
16
+ const taskItems = taskItemsResult.map(item => item.node);
17
+ const taskItemFragments = taskItems.map(taskItem => taskItem.content);
18
+ let targetNodes = [];
19
+
20
+ // Convert fragments to headings if target is heading
21
+ if (targetNodeType === schema.nodes.heading && targetAttrs) {
22
+ // convert the fragments to headings
23
+ const targetHeadingLevel = targetAttrs.level;
24
+ targetNodes = taskItemFragments.map(fragment => schema.nodes.heading.createChecked({
25
+ level: targetHeadingLevel
26
+ }, fragment.content));
27
+ }
28
+
29
+ // Convert fragments to paragraphs if target is paragraphs
30
+ if (targetNodeType === schema.nodes.paragraph) {
31
+ // convert the fragments to paragraphs
32
+ targetNodes = taskItemFragments.map(fragment => schema.nodes.paragraph.createChecked({}, fragment.content));
33
+ }
34
+
35
+ // Convert fragments to code block if target is code block
36
+ if (targetNodeType === schema.nodes.codeBlock) {
37
+ // convert the fragments to one code block
38
+ const codeBlockContent = taskItemFragments.map(fragment => fragment.textBetween(0, fragment.size, '\n')).join('\n');
39
+ targetNodes = [schema.nodes.codeBlock.createChecked({}, schema.text(codeBlockContent))];
40
+ }
41
+
42
+ // Replace the task list node with the new content in the transaction
43
+ const slice = new Slice(Fragment.fromArray(targetNodes), 0, 0);
44
+ const rangeStart = sourcePos !== null ? sourcePos : selection.from;
45
+ tr.replaceRange(rangeStart, rangeStart + sourceNode.nodeSize, slice);
46
+ return tr;
47
+ };
@@ -1,16 +1,19 @@
1
1
  import { findWrapping } from '@atlaskit/editor-prosemirror/transform';
2
2
  import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
3
+ import { transformListStructure } from './list/transformBetweenListTypes';
4
+ import { transformOrderedUnorderedListToBlockNodes } from './list/transformOrderedUnorderedListToBlockNodes';
5
+ import { transformTaskListToBlockNodes } from './list/transformTaskListToBlockNodes';
3
6
  import { isBlockNodeType, isContainerNodeType, isListNodeType } from './utils';
4
7
 
5
8
  /**
6
9
  * Transform selection to list type
7
10
  */
8
- export const transformToList = ({
9
- tr,
10
- targetNodeType,
11
- targetAttrs
12
- }) => {
13
- // Wrap selection in list of target type
11
+ export const transformBlockToList = context => {
12
+ const {
13
+ tr,
14
+ targetNodeType,
15
+ targetAttrs
16
+ } = context;
14
17
  const {
15
18
  $from,
16
19
  $to
@@ -19,16 +22,49 @@ export const transformToList = ({
19
22
  if (!range) {
20
23
  return null;
21
24
  }
25
+ const {
26
+ nodes
27
+ } = tr.doc.type.schema;
28
+ const isTargetTask = targetNodeType === nodes.taskList;
29
+
30
+ // Handle task lists differently due to their structure
31
+ // TODO: ED-29152 - Implement task list transformation
32
+ if (isTargetTask) {
33
+ return null;
34
+ }
35
+
36
+ // For headings, convert to paragraph first since headings cannot be direct children of list items
37
+ const sourceNode = tr.doc.nodeAt(range.start);
38
+ if (sourceNode && sourceNode.type.name.startsWith('heading')) {
39
+ tr.setBlockType(range.start, range.end, nodes.paragraph);
40
+ }
22
41
 
23
- // Find if we can wrap the selection in the target list type
24
- const wrapping = findWrapping(range, targetNodeType, targetAttrs);
42
+ // Get the current range (updated if we converted from heading)
43
+ const currentRange = tr.selection.$from.blockRange(tr.selection.$to) || range;
44
+
45
+ // Wrap in the target list type
46
+ const wrapping = findWrapping(currentRange, targetNodeType, targetAttrs);
25
47
  if (!wrapping) {
26
48
  return null;
27
49
  }
28
- tr.wrap(range, wrapping);
50
+ tr.wrap(currentRange, wrapping);
29
51
  return tr;
30
52
  };
31
53
 
54
+ /**
55
+ * Transform list to block nodes
56
+ */
57
+ export const transformListToBlockNodes = context => {
58
+ const {
59
+ sourceNode
60
+ } = context;
61
+ if (sourceNode.type.name === 'taskList') {
62
+ return transformTaskListToBlockNodes(context);
63
+ } else {
64
+ return transformOrderedUnorderedListToBlockNodes(context);
65
+ }
66
+ };
67
+
32
68
  /**
33
69
  * Transform list nodes
34
70
  */
@@ -39,7 +75,7 @@ export const transformListNode = context => {
39
75
  // Transform list to block type
40
76
  if (isBlockNodeType(targetNodeType)) {
41
77
  // Lift list items out of the list and convert to target block type
42
- return null;
78
+ return transformListToBlockNodes(context);
43
79
  }
44
80
 
45
81
  // Transform list to container type
@@ -77,16 +113,30 @@ export const transformBetweenListTypes = ({
77
113
  nodes
78
114
  } = tr.doc.type.schema;
79
115
 
80
- // Find the list node
81
- const listNode = findParentNodeOfType([nodes.bulletList, nodes.orderedList, nodes.taskList])(selection);
116
+ // Find the list node - support bullet lists, ordered lists, and task lists
117
+ const supportedListTypes = [nodes.bulletList, nodes.orderedList, nodes.taskList].filter(Boolean); // Filter out undefined nodes in case some schemas don't have all types
118
+
119
+ const listNode = findParentNodeOfType(supportedListTypes)(selection);
82
120
  if (!listNode) {
83
121
  return null;
84
122
  }
123
+ const sourceListType = listNode.node.type;
124
+ const isSourceBulletOrOrdered = sourceListType === nodes.bulletList || sourceListType === nodes.orderedList;
125
+ const isTargetTask = targetNodeType === nodes.taskList;
126
+ const isSourceTask = sourceListType === nodes.taskList;
127
+ const isTargetBulletOrOrdered = targetNodeType === nodes.bulletList || targetNodeType === nodes.orderedList;
128
+
129
+ // Check if we need structure transformation
130
+ const needsStructureTransform = isSourceBulletOrOrdered && isTargetTask || isSourceTask && isTargetBulletOrOrdered;
85
131
  try {
86
- // Change the list type while preserving content
87
- tr.setNodeMarkup(listNode.pos, targetNodeType);
88
- return tr;
89
- } catch (e) {
132
+ if (!needsStructureTransform) {
133
+ // Simple type change for same structure lists (bullet <-> ordered)
134
+ tr.setNodeMarkup(listNode.pos, targetNodeType);
135
+ } else {
136
+ tr = transformListStructure(tr, listNode, targetNodeType, nodes);
137
+ }
138
+ } catch {
90
139
  return null;
91
140
  }
141
+ return tr;
92
142
  };
@@ -3,7 +3,6 @@ import { MOVE_UP_MENU_ITEM, MOVE_UP_DOWN_MENU_SECTION, MOVE_DOWN_MENU_ITEM, MOVE
3
3
  import { ToolbarDropdownItemSection, ToolbarNestedDropdownMenu } from '@atlaskit/editor-toolbar';
4
4
  import ChangesIcon from '@atlaskit/icon/core/changes';
5
5
  import ChevronRightIcon from '@atlaskit/icon/core/chevron-right';
6
- import { fg } from '@atlaskit/platform-feature-flags';
7
6
  import CopyBlockMenuItem from './copy-block';
8
7
  import { CopyLinkDropdownItem } from './copy-link';
9
8
  import { DeleteDropdownItem } from './delete-button';
@@ -88,7 +87,7 @@ export const getBlockMenuComponents = ({
88
87
  api,
89
88
  config
90
89
  }) => {
91
- return [...(fg('platform_editor_block_menu_format') ? getFormatMenuComponents() : []), {
90
+ return [...getFormatMenuComponents(), {
92
91
  type: 'block-menu-section',
93
92
  key: COPY_MENU_SECTION.key,
94
93
  rank: BLOCK_MENU_SECTION_RANK[COPY_MENU_SECTION.key],
@@ -1,3 +1,3 @@
1
1
 
2
- ._2rkoglpi{border-radius:var(--ds-border-radius,4px)}._16qs1cd0{box-shadow:var(--ds-shadow-overlay,0 8px 9pt #091e4226,0 0 1px #091e424f)}
2
+ ._2rko12b0{border-radius:var(--ds-radius-small,4px)}._16qs1cd0{box-shadow:var(--ds-shadow-overlay,0 8px 9pt #091e4226,0 0 1px #091e424f)}
3
3
  ._bfhk1bhr{background-color:var(--ds-surface-overlay,#fff)}
@@ -13,7 +13,7 @@ import { ToolbarDropdownItem, ToolbarDropdownItemSection, ToolbarNestedDropdownM
13
13
  import { Box } from '@atlaskit/primitives/compiled';
14
14
  import { BlockMenuRenderer } from './block-menu-renderer';
15
15
  const styles = {
16
- base: "_2rkoglpi _bfhk1bhr _16qs1cd0"
16
+ base: "_2rko12b0 _bfhk1bhr _16qs1cd0"
17
17
  };
18
18
  const DEFAULT_MENU_WIDTH = 230;
19
19
  const DRAG_HANDLE_OFFSET_PADDING = 5;
@@ -21,19 +21,22 @@ export var formatNode = function formatNode(targetType) {
21
21
  nodePos = selectedNode.pos;
22
22
  } else {
23
23
  // Try to find parent node (including list parents)
24
- var parentNode = findParentNodeOfType([nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.listItem, nodes.layoutSection])(selection);
24
+ var parentNode = findParentNodeOfType([nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.listItem, nodes.taskItem, nodes.layoutSection])(selection);
25
25
  if (parentNode) {
26
26
  nodeToFormat = parentNode.node;
27
27
  nodePos = parentNode.pos;
28
-
28
+ var paragraphOrHeadingNode = findParentNodeOfType([nodes.paragraph, nodes.heading])(selection);
29
29
  // Special case: if we found a listItem, check if we need the parent list instead
30
- if (parentNode.node.type === nodes.listItem) {
30
+ if (parentNode.node.type === nodes.listItem || parentNode.node.type === nodes.taskItem) {
31
31
  var listParent = findParentNodeOfType([nodes.bulletList, nodes.orderedList, nodes.taskList])(selection);
32
32
  if (listParent) {
33
33
  // For list transformations, we want the list parent, not the listItem
34
34
  nodeToFormat = listParent.node;
35
35
  nodePos = listParent.pos;
36
36
  }
37
+ } else if (paragraphOrHeadingNode) {
38
+ nodeToFormat = paragraphOrHeadingNode.node;
39
+ nodePos = paragraphOrHeadingNode.pos;
37
40
  }
38
41
  }
39
42
  }
@@ -1,32 +1,44 @@
1
1
  import { transformToContainer } from './container-transforms';
2
- import { transformToList } from './list-transforms';
2
+ import { getInlineNodeTextContent } from './inline-node-transforms';
3
+ import { transformBlockToList } from './list-transforms';
3
4
  import { isListNodeType, isContainerNodeType, isBlockNodeType } from './utils';
4
5
 
5
6
  /**
6
7
  * Transform block nodes (paragraph, heading, codeblock)
7
8
  */
8
9
  export var transformBlockNode = function transformBlockNode(context) {
9
- var tr = context.tr,
10
- targetNodeType = context.targetNodeType,
11
- targetAttrs = context.targetAttrs;
12
- var selection = tr.selection;
13
- var $from = selection.$from,
14
- $to = selection.$to;
10
+ var targetNodeType = context.targetNodeType;
15
11
 
16
12
  // Handle transformation to list types
17
13
  if (isListNodeType(targetNodeType)) {
18
- return transformToList(context);
14
+ return transformBlockToList(context);
19
15
  }
20
16
 
21
17
  // Handle transformation to container types (panel, expand, blockquote)
22
18
  if (isContainerNodeType(targetNodeType)) {
23
- return transformToContainer();
19
+ return transformToContainer(context);
24
20
  }
25
21
 
26
22
  // Handle block type transformation (paragraph, heading, codeblock)
27
23
  if (isBlockNodeType(targetNodeType)) {
28
- tr.setBlockType($from.pos, $to.pos, targetNodeType, targetAttrs);
29
- return tr;
24
+ return transformToBlockNode(context);
30
25
  }
31
26
  return null;
27
+ };
28
+ var transformToBlockNode = function transformToBlockNode(context) {
29
+ var tr = context.tr,
30
+ targetNodeType = context.targetNodeType,
31
+ targetAttrs = context.targetAttrs;
32
+ var selection = tr.selection,
33
+ doc = tr.doc;
34
+ var $from = selection.$from,
35
+ $to = selection.$to;
36
+ var schema = doc.type.schema;
37
+ if (targetNodeType === schema.nodes.codeBlock) {
38
+ var textContent = getInlineNodeTextContent(selection.content().content, tr);
39
+ var node = schema.nodes.codeBlock.createChecked(undefined, textContent);
40
+ return tr.replaceRangeWith(selection.from, selection.to, node);
41
+ }
42
+ tr.setBlockType($from.pos, $to.pos, targetNodeType, targetAttrs);
43
+ return tr;
32
44
  };
@@ -1,20 +1,52 @@
1
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
1
2
  import { isBlockNodeType, isListNodeType, isContainerNodeType } from './utils';
3
+ var convertInvalidNodeToValidNodeType = function convertInvalidNodeToValidNodeType(sourceContent, sourceNodeType, validNodeType, withMarks) {
4
+ var validTransformedContent = [];
5
+ // Headings are not valid inside headings so convert heading nodes to paragraphs
6
+ sourceContent.forEach(function (node) {
7
+ if (sourceNodeType === node.type) {
8
+ validTransformedContent.push(validNodeType.createChecked(node.attrs, node.content, withMarks ? node.marks : undefined));
9
+ } else {
10
+ validTransformedContent.push(node);
11
+ }
12
+ });
13
+ return Fragment.from(validTransformedContent);
14
+ };
2
15
 
3
16
  /**
4
17
  * Transform selection to container type
5
18
  */
6
- export var transformToContainer = function transformToContainer() {
7
- return null;
19
+ export var transformToContainer = function transformToContainer(_ref) {
20
+ var tr = _ref.tr,
21
+ sourceNode = _ref.sourceNode,
22
+ targetNodeType = _ref.targetNodeType,
23
+ targetAttrs = _ref.targetAttrs;
24
+ var selection = tr.selection;
25
+ var schema = tr.doc.type.schema;
26
+ var content = selection.content().content;
27
+ var transformedContent = content;
28
+ if (sourceNode.type === schema.nodes.codeBlock) {
29
+ transformedContent = convertInvalidNodeToValidNodeType(transformedContent, schema.nodes.codeBlock, schema.nodes.paragraph);
30
+ }
31
+ if (targetNodeType === schema.nodes.blockquote) {
32
+ transformedContent = convertInvalidNodeToValidNodeType(transformedContent, schema.nodes.heading, schema.nodes.paragraph, true);
33
+ }
34
+ var newNode = targetNodeType.createAndFill(targetAttrs, transformedContent);
35
+ if (!newNode) {
36
+ return null;
37
+ }
38
+ tr.replaceRangeWith(selection.from, selection.to, newNode);
39
+ return tr;
8
40
  };
9
41
 
10
42
  /**
11
43
  * Transform container nodes (panel, expand, blockquote)
12
44
  */
13
- export var transformContainerNode = function transformContainerNode(_ref) {
14
- var tr = _ref.tr,
15
- sourcePos = _ref.sourcePos,
16
- targetNodeType = _ref.targetNodeType,
17
- targetAttrs = _ref.targetAttrs;
45
+ export var transformContainerNode = function transformContainerNode(_ref2) {
46
+ var tr = _ref2.tr,
47
+ sourcePos = _ref2.sourcePos,
48
+ targetNodeType = _ref2.targetNodeType,
49
+ targetAttrs = _ref2.targetAttrs;
18
50
  if (sourcePos === null) {
19
51
  return null;
20
52
  }
@@ -0,0 +1,21 @@
1
+ export var getInlineNodeTextContent = function getInlineNodeTextContent(sourceContent, tr) {
2
+ var validTransformedContent = '';
3
+ var schema = tr.doc.type.schema;
4
+ if (sourceContent.content.length > 1) {
5
+ return;
6
+ }
7
+ // Headings are not valid inside headings so convert heading nodes to paragraphs
8
+ sourceContent.forEach(function (node) {
9
+ if (['paragraph', 'heading'].includes(node.type.name)) {
10
+ node.content.forEach(function (inlineNode) {
11
+ if (inlineNode.type.name === 'status') {
12
+ validTransformedContent += inlineNode.attrs.text;
13
+ } else {
14
+ validTransformedContent += "".concat(inlineNode.textContent);
15
+ }
16
+ });
17
+ validTransformedContent;
18
+ }
19
+ });
20
+ return schema.text(validTransformedContent);
21
+ };