@atlaskit/editor-plugin-block-menu 1.0.0 → 1.0.1
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 +8 -0
- package/dist/cjs/editor-commands/formatNode.js +6 -3
- package/dist/cjs/editor-commands/transforms/block-transforms.js +22 -10
- package/dist/cjs/editor-commands/transforms/container-transforms.js +40 -7
- package/dist/cjs/editor-commands/transforms/inline-node-transforms.js +27 -0
- package/dist/cjs/editor-commands/transforms/list/transformBetweenListTypes.js +102 -0
- package/dist/cjs/editor-commands/transforms/list-transforms.js +51 -19
- package/dist/cjs/ui/block-menu-components.js +1 -2
- package/dist/es2019/editor-commands/formatNode.js +6 -3
- package/dist/es2019/editor-commands/transforms/block-transforms.js +29 -15
- package/dist/es2019/editor-commands/transforms/container-transforms.js +35 -2
- package/dist/es2019/editor-commands/transforms/inline-node-transforms.js +21 -0
- package/dist/es2019/editor-commands/transforms/list/transformBetweenListTypes.js +98 -0
- package/dist/es2019/editor-commands/transforms/list-transforms.js +49 -15
- package/dist/es2019/ui/block-menu-components.js +1 -2
- package/dist/esm/editor-commands/formatNode.js +6 -3
- package/dist/esm/editor-commands/transforms/block-transforms.js +23 -11
- package/dist/esm/editor-commands/transforms/container-transforms.js +39 -7
- package/dist/esm/editor-commands/transforms/inline-node-transforms.js +21 -0
- package/dist/esm/editor-commands/transforms/list/transformBetweenListTypes.js +95 -0
- package/dist/esm/editor-commands/transforms/list-transforms.js +50 -18
- package/dist/esm/ui/block-menu-components.js +1 -2
- package/dist/types/editor-commands/transforms/container-transforms.d.ts +2 -2
- package/dist/types/editor-commands/transforms/inline-node-transforms.d.ts +3 -0
- package/dist/types/editor-commands/transforms/list/transformBetweenListTypes.d.ts +9 -0
- package/dist/types/editor-commands/transforms/list-transforms.d.ts +1 -6
- package/dist/types-ts4.5/editor-commands/transforms/container-transforms.d.ts +2 -2
- package/dist/types-ts4.5/editor-commands/transforms/inline-node-transforms.d.ts +3 -0
- package/dist/types-ts4.5/editor-commands/transforms/list/transformBetweenListTypes.d.ts +9 -0
- package/dist/types-ts4.5/editor-commands/transforms/list-transforms.d.ts +1 -6
- package/package.json +3 -8
|
@@ -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
|
+
};
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { findWrapping } from '@atlaskit/editor-prosemirror/transform';
|
|
2
2
|
import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
|
|
3
|
+
import { transformListStructure } from './list/transformBetweenListTypes';
|
|
3
4
|
import { isBlockNodeType, isContainerNodeType, isListNodeType } from './utils';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Transform selection to list type
|
|
7
8
|
*/
|
|
8
|
-
export const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
export const transformBlockToList = context => {
|
|
10
|
+
const {
|
|
11
|
+
tr,
|
|
12
|
+
targetNodeType,
|
|
13
|
+
targetAttrs
|
|
14
|
+
} = context;
|
|
14
15
|
const {
|
|
15
16
|
$from,
|
|
16
17
|
$to
|
|
@@ -19,13 +20,32 @@ export const transformToList = ({
|
|
|
19
20
|
if (!range) {
|
|
20
21
|
return null;
|
|
21
22
|
}
|
|
23
|
+
const {
|
|
24
|
+
nodes
|
|
25
|
+
} = tr.doc.type.schema;
|
|
26
|
+
const isTargetTask = targetNodeType === nodes.taskList;
|
|
27
|
+
|
|
28
|
+
// Handle task lists differently due to their structure
|
|
29
|
+
// TODO: ED-29152 - Implement task list transformation
|
|
30
|
+
if (isTargetTask) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// For headings, convert to paragraph first since headings cannot be direct children of list items
|
|
35
|
+
const sourceNode = tr.doc.nodeAt(range.start);
|
|
36
|
+
if (sourceNode && sourceNode.type.name.startsWith('heading')) {
|
|
37
|
+
tr.setBlockType(range.start, range.end, nodes.paragraph);
|
|
38
|
+
}
|
|
22
39
|
|
|
23
|
-
//
|
|
24
|
-
const
|
|
40
|
+
// Get the current range (updated if we converted from heading)
|
|
41
|
+
const currentRange = tr.selection.$from.blockRange(tr.selection.$to) || range;
|
|
42
|
+
|
|
43
|
+
// Wrap in the target list type
|
|
44
|
+
const wrapping = findWrapping(currentRange, targetNodeType, targetAttrs);
|
|
25
45
|
if (!wrapping) {
|
|
26
46
|
return null;
|
|
27
47
|
}
|
|
28
|
-
tr.wrap(
|
|
48
|
+
tr.wrap(currentRange, wrapping);
|
|
29
49
|
return tr;
|
|
30
50
|
};
|
|
31
51
|
|
|
@@ -77,16 +97,30 @@ export const transformBetweenListTypes = ({
|
|
|
77
97
|
nodes
|
|
78
98
|
} = tr.doc.type.schema;
|
|
79
99
|
|
|
80
|
-
// Find the list node
|
|
81
|
-
const
|
|
100
|
+
// Find the list node - support bullet lists, ordered lists, and task lists
|
|
101
|
+
const supportedListTypes = [nodes.bulletList, nodes.orderedList, nodes.taskList].filter(Boolean); // Filter out undefined nodes in case some schemas don't have all types
|
|
102
|
+
|
|
103
|
+
const listNode = findParentNodeOfType(supportedListTypes)(selection);
|
|
82
104
|
if (!listNode) {
|
|
83
105
|
return null;
|
|
84
106
|
}
|
|
107
|
+
const sourceListType = listNode.node.type;
|
|
108
|
+
const isSourceBulletOrOrdered = sourceListType === nodes.bulletList || sourceListType === nodes.orderedList;
|
|
109
|
+
const isTargetTask = targetNodeType === nodes.taskList;
|
|
110
|
+
const isSourceTask = sourceListType === nodes.taskList;
|
|
111
|
+
const isTargetBulletOrOrdered = targetNodeType === nodes.bulletList || targetNodeType === nodes.orderedList;
|
|
112
|
+
|
|
113
|
+
// Check if we need structure transformation
|
|
114
|
+
const needsStructureTransform = isSourceBulletOrOrdered && isTargetTask || isSourceTask && isTargetBulletOrOrdered;
|
|
85
115
|
try {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
116
|
+
if (!needsStructureTransform) {
|
|
117
|
+
// Simple type change for same structure lists (bullet <-> ordered)
|
|
118
|
+
tr.setNodeMarkup(listNode.pos, targetNodeType);
|
|
119
|
+
} else {
|
|
120
|
+
tr = transformListStructure(tr, listNode, targetNodeType, nodes);
|
|
121
|
+
}
|
|
122
|
+
} catch {
|
|
90
123
|
return null;
|
|
91
124
|
}
|
|
125
|
+
return tr;
|
|
92
126
|
};
|
|
@@ -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 [...
|
|
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],
|
|
@@ -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 {
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
14
|
-
var tr =
|
|
15
|
-
sourcePos =
|
|
16
|
-
targetNodeType =
|
|
17
|
-
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
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
2
|
+
/**
|
|
3
|
+
* Extract all inline content from a node
|
|
4
|
+
*/
|
|
5
|
+
var extractInlineContent = function extractInlineContent(node) {
|
|
6
|
+
var inlineContent = [];
|
|
7
|
+
for (var i = 0; i < node.childCount; i++) {
|
|
8
|
+
inlineContent.push(node.child(i));
|
|
9
|
+
}
|
|
10
|
+
return inlineContent;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Transform list structure
|
|
15
|
+
*/
|
|
16
|
+
export var transformListStructure = function transformListStructure(tr, listNode, targetNodeType, nodes) {
|
|
17
|
+
try {
|
|
18
|
+
var sourceList = listNode.node,
|
|
19
|
+
listPos = listNode.pos;
|
|
20
|
+
var bulletList = nodes.bulletList,
|
|
21
|
+
orderedList = nodes.orderedList,
|
|
22
|
+
taskList = nodes.taskList,
|
|
23
|
+
listItem = nodes.listItem,
|
|
24
|
+
taskItem = nodes.taskItem,
|
|
25
|
+
paragraph = nodes.paragraph;
|
|
26
|
+
var isSourceBulletOrOrdered = sourceList.type === bulletList || sourceList.type === orderedList;
|
|
27
|
+
var isTargetTask = targetNodeType === taskList;
|
|
28
|
+
var isSourceTask = sourceList.type === taskList;
|
|
29
|
+
var newListItems = [];
|
|
30
|
+
var listStart = listPos;
|
|
31
|
+
var listEnd = listPos + sourceList.nodeSize;
|
|
32
|
+
|
|
33
|
+
// Use nodesBetween to efficiently traverse the list structure
|
|
34
|
+
tr.doc.nodesBetween(listStart, listEnd, function (node, pos, parent) {
|
|
35
|
+
// Only process direct children of the list (depth 1)
|
|
36
|
+
if (parent !== sourceList) {
|
|
37
|
+
return true; // Continue traversal
|
|
38
|
+
}
|
|
39
|
+
if (isSourceBulletOrOrdered && isTargetTask) {
|
|
40
|
+
// Converting from bullet/ordered list to task list
|
|
41
|
+
// Extract inline content from all children within listItem
|
|
42
|
+
if (node.type === listItem) {
|
|
43
|
+
var inlineContent = [];
|
|
44
|
+
|
|
45
|
+
// Extract all inline content from all child nodes
|
|
46
|
+
for (var i = 0; i < node.childCount; i++) {
|
|
47
|
+
var child = node.child(i);
|
|
48
|
+
if (child.type === paragraph) {
|
|
49
|
+
// Extract inline content from paragraphs
|
|
50
|
+
inlineContent.push.apply(inlineContent, _toConsumableArray(extractInlineContent(child)));
|
|
51
|
+
} else if (child.isBlock) {
|
|
52
|
+
// For other block content types eg. codeBlock, extract their text content and create text nodes
|
|
53
|
+
var textContent = child.textContent;
|
|
54
|
+
if (textContent) {
|
|
55
|
+
var textNode = tr.doc.type.schema.text(textContent);
|
|
56
|
+
inlineContent.push(textNode);
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
// Already inline content, add directly
|
|
60
|
+
inlineContent.push(child);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (inlineContent.length > 0) {
|
|
64
|
+
var newItem = taskItem.create(null, inlineContent);
|
|
65
|
+
newListItems.push(newItem);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} else if (isSourceTask && !isTargetTask) {
|
|
69
|
+
// Converting from task list to bullet/ordered list
|
|
70
|
+
// Structure: taskItem > inline content -> listItem > paragraph > inline content
|
|
71
|
+
if (node.type === taskItem) {
|
|
72
|
+
var _inlineContent = extractInlineContent(node);
|
|
73
|
+
if (_inlineContent.length > 0) {
|
|
74
|
+
var paragraphNode = paragraph.create(null, _inlineContent);
|
|
75
|
+
var newListItem = listItem.create(null, paragraphNode);
|
|
76
|
+
newListItems.push(newListItem);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return false; // Don't traverse into children of list items
|
|
81
|
+
});
|
|
82
|
+
if (newListItems.length === 0) {
|
|
83
|
+
return tr;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Create new list with transformed items
|
|
87
|
+
var newList = targetNodeType.create(null, newListItems);
|
|
88
|
+
|
|
89
|
+
// Replace the entire list
|
|
90
|
+
tr.replaceWith(listStart, listEnd, newList);
|
|
91
|
+
return tr;
|
|
92
|
+
} catch (_unused) {
|
|
93
|
+
return tr;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { findWrapping } from '@atlaskit/editor-prosemirror/transform';
|
|
2
2
|
import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
|
|
3
|
+
import { transformListStructure } from './list/transformBetweenListTypes';
|
|
3
4
|
import { isBlockNodeType, isContainerNodeType, isListNodeType } from './utils';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Transform selection to list type
|
|
7
8
|
*/
|
|
8
|
-
export var
|
|
9
|
-
var tr =
|
|
10
|
-
targetNodeType =
|
|
11
|
-
targetAttrs =
|
|
12
|
-
// Wrap selection in list of target type
|
|
9
|
+
export var transformBlockToList = function transformBlockToList(context) {
|
|
10
|
+
var tr = context.tr,
|
|
11
|
+
targetNodeType = context.targetNodeType,
|
|
12
|
+
targetAttrs = context.targetAttrs;
|
|
13
13
|
var _tr$selection = tr.selection,
|
|
14
14
|
$from = _tr$selection.$from,
|
|
15
15
|
$to = _tr$selection.$to;
|
|
@@ -17,13 +17,30 @@ export var transformToList = function transformToList(_ref) {
|
|
|
17
17
|
if (!range) {
|
|
18
18
|
return null;
|
|
19
19
|
}
|
|
20
|
+
var nodes = tr.doc.type.schema.nodes;
|
|
21
|
+
var isTargetTask = targetNodeType === nodes.taskList;
|
|
22
|
+
|
|
23
|
+
// Handle task lists differently due to their structure
|
|
24
|
+
// TODO: ED-29152 - Implement task list transformation
|
|
25
|
+
if (isTargetTask) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// For headings, convert to paragraph first since headings cannot be direct children of list items
|
|
30
|
+
var sourceNode = tr.doc.nodeAt(range.start);
|
|
31
|
+
if (sourceNode && sourceNode.type.name.startsWith('heading')) {
|
|
32
|
+
tr.setBlockType(range.start, range.end, nodes.paragraph);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Get the current range (updated if we converted from heading)
|
|
36
|
+
var currentRange = tr.selection.$from.blockRange(tr.selection.$to) || range;
|
|
20
37
|
|
|
21
|
-
//
|
|
22
|
-
var wrapping = findWrapping(
|
|
38
|
+
// Wrap in the target list type
|
|
39
|
+
var wrapping = findWrapping(currentRange, targetNodeType, targetAttrs);
|
|
23
40
|
if (!wrapping) {
|
|
24
41
|
return null;
|
|
25
42
|
}
|
|
26
|
-
tr.wrap(
|
|
43
|
+
tr.wrap(currentRange, wrapping);
|
|
27
44
|
return tr;
|
|
28
45
|
};
|
|
29
46
|
|
|
@@ -62,22 +79,37 @@ export var liftListToBlockType = function liftListToBlockType() {
|
|
|
62
79
|
/**
|
|
63
80
|
* Transform between different list types
|
|
64
81
|
*/
|
|
65
|
-
export var transformBetweenListTypes = function transformBetweenListTypes(
|
|
66
|
-
var tr =
|
|
67
|
-
targetNodeType =
|
|
68
|
-
var
|
|
82
|
+
export var transformBetweenListTypes = function transformBetweenListTypes(_ref) {
|
|
83
|
+
var tr = _ref.tr,
|
|
84
|
+
targetNodeType = _ref.targetNodeType;
|
|
85
|
+
var _tr = tr,
|
|
86
|
+
selection = _tr.selection;
|
|
69
87
|
var nodes = tr.doc.type.schema.nodes;
|
|
70
88
|
|
|
71
|
-
// Find the list node
|
|
72
|
-
var
|
|
89
|
+
// Find the list node - support bullet lists, ordered lists, and task lists
|
|
90
|
+
var supportedListTypes = [nodes.bulletList, nodes.orderedList, nodes.taskList].filter(Boolean); // Filter out undefined nodes in case some schemas don't have all types
|
|
91
|
+
|
|
92
|
+
var listNode = findParentNodeOfType(supportedListTypes)(selection);
|
|
73
93
|
if (!listNode) {
|
|
74
94
|
return null;
|
|
75
95
|
}
|
|
96
|
+
var sourceListType = listNode.node.type;
|
|
97
|
+
var isSourceBulletOrOrdered = sourceListType === nodes.bulletList || sourceListType === nodes.orderedList;
|
|
98
|
+
var isTargetTask = targetNodeType === nodes.taskList;
|
|
99
|
+
var isSourceTask = sourceListType === nodes.taskList;
|
|
100
|
+
var isTargetBulletOrOrdered = targetNodeType === nodes.bulletList || targetNodeType === nodes.orderedList;
|
|
101
|
+
|
|
102
|
+
// Check if we need structure transformation
|
|
103
|
+
var needsStructureTransform = isSourceBulletOrOrdered && isTargetTask || isSourceTask && isTargetBulletOrOrdered;
|
|
76
104
|
try {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
105
|
+
if (!needsStructureTransform) {
|
|
106
|
+
// Simple type change for same structure lists (bullet <-> ordered)
|
|
107
|
+
tr.setNodeMarkup(listNode.pos, targetNodeType);
|
|
108
|
+
} else {
|
|
109
|
+
tr = transformListStructure(tr, listNode, targetNodeType, nodes);
|
|
110
|
+
}
|
|
111
|
+
} catch (_unused) {
|
|
81
112
|
return null;
|
|
82
113
|
}
|
|
114
|
+
return tr;
|
|
83
115
|
};
|
|
@@ -4,7 +4,6 @@ import { MOVE_UP_MENU_ITEM, MOVE_UP_DOWN_MENU_SECTION, MOVE_DOWN_MENU_ITEM, MOVE
|
|
|
4
4
|
import { ToolbarDropdownItemSection, ToolbarNestedDropdownMenu } from '@atlaskit/editor-toolbar';
|
|
5
5
|
import ChangesIcon from '@atlaskit/icon/core/changes';
|
|
6
6
|
import ChevronRightIcon from '@atlaskit/icon/core/chevron-right';
|
|
7
|
-
import { fg } from '@atlaskit/platform-feature-flags';
|
|
8
7
|
import CopyBlockMenuItem from './copy-block';
|
|
9
8
|
import { CopyLinkDropdownItem } from './copy-link';
|
|
10
9
|
import { DeleteDropdownItem } from './delete-button';
|
|
@@ -91,7 +90,7 @@ var getFormatMenuComponents = function getFormatMenuComponents() {
|
|
|
91
90
|
export var getBlockMenuComponents = function getBlockMenuComponents(_ref4) {
|
|
92
91
|
var api = _ref4.api,
|
|
93
92
|
config = _ref4.config;
|
|
94
|
-
return [].concat(_toConsumableArray(
|
|
93
|
+
return [].concat(_toConsumableArray(getFormatMenuComponents()), [{
|
|
95
94
|
type: 'block-menu-section',
|
|
96
95
|
key: COPY_MENU_SECTION.key,
|
|
97
96
|
rank: BLOCK_MENU_SECTION_RANK[COPY_MENU_SECTION.key],
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { TransformFunction } from './types';
|
|
1
|
+
import type { TransformContext, TransformFunction } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Transform selection to container type
|
|
4
4
|
*/
|
|
5
|
-
export declare const transformToContainer: () => null;
|
|
5
|
+
export declare const transformToContainer: ({ tr, sourceNode, targetNodeType, targetAttrs, }: TransformContext) => import("prosemirror-state").Transaction | null;
|
|
6
6
|
/**
|
|
7
7
|
* Transform container nodes (panel, expand, blockquote)
|
|
8
8
|
*/
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { Fragment } from '@atlaskit/editor-prosemirror/model';
|
|
2
|
+
import type { Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
3
|
+
export declare const getInlineNodeTextContent: (sourceContent: Fragment, tr: Transaction) => import("prosemirror-model").Node | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Node as PMNode, NodeType } from '@atlaskit/editor-prosemirror/model';
|
|
2
|
+
import type { Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
3
|
+
/**
|
|
4
|
+
* Transform list structure
|
|
5
|
+
*/
|
|
6
|
+
export declare const transformListStructure: (tr: Transaction, listNode: {
|
|
7
|
+
node: PMNode;
|
|
8
|
+
pos: number;
|
|
9
|
+
}, targetNodeType: NodeType, nodes: Record<string, NodeType>) => Transaction;
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import type { NodeType } from '@atlaskit/editor-prosemirror/model';
|
|
2
1
|
import type { Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
3
2
|
import type { TransformContext, TransformFunction } from './types';
|
|
4
3
|
/**
|
|
5
4
|
* Transform selection to list type
|
|
6
5
|
*/
|
|
7
|
-
export declare const
|
|
8
|
-
targetAttrs?: Record<string, unknown>;
|
|
9
|
-
targetNodeType: NodeType;
|
|
10
|
-
tr: Transaction;
|
|
11
|
-
}) => Transaction | null;
|
|
6
|
+
export declare const transformBlockToList: (context: TransformContext) => Transaction | null;
|
|
12
7
|
/**
|
|
13
8
|
* Transform list nodes
|
|
14
9
|
*/
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { TransformFunction } from './types';
|
|
1
|
+
import type { TransformContext, TransformFunction } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Transform selection to container type
|
|
4
4
|
*/
|
|
5
|
-
export declare const transformToContainer: () => null;
|
|
5
|
+
export declare const transformToContainer: ({ tr, sourceNode, targetNodeType, targetAttrs, }: TransformContext) => import("prosemirror-state").Transaction | null;
|
|
6
6
|
/**
|
|
7
7
|
* Transform container nodes (panel, expand, blockquote)
|
|
8
8
|
*/
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { Fragment } from '@atlaskit/editor-prosemirror/model';
|
|
2
|
+
import type { Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
3
|
+
export declare const getInlineNodeTextContent: (sourceContent: Fragment, tr: Transaction) => import("prosemirror-model").Node | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Node as PMNode, NodeType } from '@atlaskit/editor-prosemirror/model';
|
|
2
|
+
import type { Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
3
|
+
/**
|
|
4
|
+
* Transform list structure
|
|
5
|
+
*/
|
|
6
|
+
export declare const transformListStructure: (tr: Transaction, listNode: {
|
|
7
|
+
node: PMNode;
|
|
8
|
+
pos: number;
|
|
9
|
+
}, targetNodeType: NodeType, nodes: Record<string, NodeType>) => Transaction;
|