@atlaskit/editor-plugin-block-menu 0.0.12 → 0.0.14
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 +15 -0
- package/afm-cc/tsconfig.json +4 -1
- package/afm-dev-agents/tsconfig.json +3 -0
- package/afm-jira/tsconfig.json +3 -0
- package/afm-passionfruit/tsconfig.json +3 -0
- package/afm-post-office/tsconfig.json +3 -0
- package/afm-rovo-extension/tsconfig.json +3 -0
- package/afm-townsquare/tsconfig.json +3 -0
- package/dist/cjs/blockMenuPlugin.js +6 -0
- package/dist/cjs/editor-commands/formatNode.js +23 -0
- package/dist/cjs/editor-commands/transforms/block-transforms.js +37 -0
- package/dist/cjs/editor-commands/transforms/container-transforms.js +59 -0
- package/dist/cjs/editor-commands/transforms/list-transforms.js +53 -0
- package/dist/cjs/editor-commands/transforms/transformNodeToTargetType.js +50 -0
- package/dist/cjs/editor-commands/transforms/types.js +5 -0
- package/dist/cjs/editor-commands/transforms/utils.js +109 -0
- package/dist/cjs/ui/block-menu-components.js +55 -42
- package/dist/cjs/ui/block-menu-renderer.js +48 -11
- package/dist/es2019/blockMenuPlugin.js +6 -0
- package/dist/es2019/editor-commands/formatNode.js +20 -0
- package/dist/es2019/editor-commands/transforms/block-transforms.js +38 -0
- package/dist/es2019/editor-commands/transforms/container-transforms.js +55 -0
- package/dist/es2019/editor-commands/transforms/list-transforms.js +49 -0
- package/dist/es2019/editor-commands/transforms/transformNodeToTargetType.js +48 -0
- package/dist/es2019/editor-commands/transforms/types.js +1 -0
- package/dist/es2019/editor-commands/transforms/utils.js +103 -0
- package/dist/es2019/ui/block-menu-components.js +45 -32
- package/dist/es2019/ui/block-menu-renderer.js +46 -11
- package/dist/esm/blockMenuPlugin.js +6 -0
- package/dist/esm/editor-commands/formatNode.js +17 -0
- package/dist/esm/editor-commands/transforms/block-transforms.js +32 -0
- package/dist/esm/editor-commands/transforms/container-transforms.js +54 -0
- package/dist/esm/editor-commands/transforms/list-transforms.js +48 -0
- package/dist/esm/editor-commands/transforms/transformNodeToTargetType.js +44 -0
- package/dist/esm/editor-commands/transforms/types.js +1 -0
- package/dist/esm/editor-commands/transforms/utils.js +103 -0
- package/dist/esm/ui/block-menu-components.js +56 -43
- package/dist/esm/ui/block-menu-renderer.js +48 -11
- package/dist/types/blockMenuPluginType.d.ts +27 -3
- package/dist/types/editor-commands/formatNode.d.ts +9 -0
- package/dist/types/editor-commands/transforms/block-transforms.d.ts +5 -0
- package/dist/types/editor-commands/transforms/container-transforms.d.ts +17 -0
- package/dist/types/editor-commands/transforms/list-transforms.d.ts +17 -0
- package/dist/types/editor-commands/transforms/transformNodeToTargetType.d.ts +4 -0
- package/dist/types/editor-commands/transforms/types.d.ts +11 -0
- package/dist/types/editor-commands/transforms/utils.d.ts +12 -0
- package/dist/types-ts4.5/blockMenuPluginType.d.ts +27 -3
- package/dist/types-ts4.5/editor-commands/formatNode.d.ts +9 -0
- package/dist/types-ts4.5/editor-commands/transforms/block-transforms.d.ts +5 -0
- package/dist/types-ts4.5/editor-commands/transforms/container-transforms.d.ts +17 -0
- package/dist/types-ts4.5/editor-commands/transforms/list-transforms.d.ts +17 -0
- package/dist/types-ts4.5/editor-commands/transforms/transformNodeToTargetType.d.ts +4 -0
- package/dist/types-ts4.5/editor-commands/transforms/types.d.ts +11 -0
- package/dist/types-ts4.5/editor-commands/transforms/utils.d.ts +12 -0
- package/package.json +13 -10
|
@@ -12,8 +12,8 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
12
12
|
var NoOp = function NoOp(props) {
|
|
13
13
|
return null;
|
|
14
14
|
};
|
|
15
|
-
var
|
|
16
|
-
return component.type === 'block-menu-section';
|
|
15
|
+
var isNonNestedMenuSection = function isNonNestedMenuSection(component) {
|
|
16
|
+
return component.type === 'block-menu-section' && !('parent' in component);
|
|
17
17
|
};
|
|
18
18
|
var isMenuItem = function isMenuItem(component) {
|
|
19
19
|
return component.type === 'block-menu-item';
|
|
@@ -21,31 +21,68 @@ var isMenuItem = function isMenuItem(component) {
|
|
|
21
21
|
var isNestedMenu = function isNestedMenu(component) {
|
|
22
22
|
return component.type === 'block-menu-nested';
|
|
23
23
|
};
|
|
24
|
+
var isNestedMenuSection = function isNestedMenuSection(component) {
|
|
25
|
+
return 'parent' in component && component.parent !== undefined && component.parent.type === 'block-menu-nested';
|
|
26
|
+
};
|
|
24
27
|
var getSortedChildren = function getSortedChildren(components, parentKey) {
|
|
25
28
|
return components.filter(function (component) {
|
|
26
|
-
return component.parent.key === parentKey;
|
|
29
|
+
return 'parent' in component && component.parent !== undefined && component.parent.key === parentKey;
|
|
27
30
|
}).sort(function (a, b) {
|
|
28
31
|
return (a.parent.rank || 0) - (b.parent.rank || 0);
|
|
29
32
|
});
|
|
30
33
|
};
|
|
31
|
-
var
|
|
32
|
-
|
|
34
|
+
var getSortedNestedSections = function getSortedNestedSections(components, parentKey) {
|
|
35
|
+
var nestedMenuSections = components.filter(isNestedMenuSection);
|
|
36
|
+
var nestedMenuSectionsWithParent = nestedMenuSections.filter(function (section) {
|
|
37
|
+
return section.parent !== undefined;
|
|
38
|
+
});
|
|
39
|
+
return getSortedChildren(nestedMenuSectionsWithParent, parentKey);
|
|
40
|
+
};
|
|
41
|
+
var getSortedNonNestedSections = function getSortedNonNestedSections(components) {
|
|
42
|
+
return components.filter(isNonNestedMenuSection).sort(function (a, b) {
|
|
33
43
|
return (a.rank || 0) - (b.rank || 0);
|
|
34
44
|
});
|
|
35
45
|
};
|
|
36
46
|
var BlockMenuRenderer = exports.BlockMenuRenderer = function BlockMenuRenderer(_ref) {
|
|
37
47
|
var components = _ref.components,
|
|
38
48
|
fallbacks = _ref.fallbacks;
|
|
39
|
-
var menuSections =
|
|
49
|
+
var menuSections = getSortedNonNestedSections(components);
|
|
40
50
|
var menuItems = components.filter(isMenuItem);
|
|
41
51
|
var nestedMenus = components.filter(isNestedMenu);
|
|
42
52
|
return /*#__PURE__*/_react.default.createElement(_react.Fragment, null, menuSections.map(function (section) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
// Get all items for the current section, including nested menus, and sort them by rank
|
|
54
|
+
var currentSectionItemsSorted = getSortedChildren([].concat((0, _toConsumableArray2.default)(menuItems), (0, _toConsumableArray2.default)(nestedMenus)), section.key);
|
|
55
|
+
|
|
56
|
+
// iterate over the current section items, if it is nested menu, get their children, sort them
|
|
57
|
+
// if they are menu items, just render as they are sorted above
|
|
58
|
+
var getChildrenWithNestedItems = function getChildrenWithNestedItems(items) {
|
|
59
|
+
return items.map(function (item) {
|
|
60
|
+
if (isNestedMenu(item)) {
|
|
61
|
+
var sortedNestedSections = getSortedNestedSections(components, item.key);
|
|
62
|
+
return sortedNestedSections.map(function (section) {
|
|
63
|
+
var sortedNestedMenuItems = getSortedChildren(menuItems, section.key);
|
|
64
|
+
var NestedMenuComponent = item.component || fallbacks.nestedMenu || NoOp;
|
|
65
|
+
var NestedSection = section.component || fallbacks.section || NoOp;
|
|
66
|
+
return /*#__PURE__*/_react.default.createElement(NestedMenuComponent, {
|
|
67
|
+
key: item.key
|
|
68
|
+
}, /*#__PURE__*/_react.default.createElement(NestedSection, {
|
|
69
|
+
key: section.key
|
|
70
|
+
}, sortedNestedMenuItems.map(function (nestedItem) {
|
|
71
|
+
var NestedMenuItemComponent = nestedItem.component || fallbacks.item || NoOp;
|
|
72
|
+
return /*#__PURE__*/_react.default.createElement(NestedMenuItemComponent, {
|
|
73
|
+
key: nestedItem.key
|
|
74
|
+
});
|
|
75
|
+
})));
|
|
76
|
+
});
|
|
77
|
+
} else {
|
|
78
|
+
var ItemComponent = item.component || fallbacks.item || NoOp;
|
|
79
|
+
return /*#__PURE__*/_react.default.createElement(ItemComponent, {
|
|
80
|
+
key: item.key
|
|
81
|
+
});
|
|
82
|
+
}
|
|
47
83
|
});
|
|
48
|
-
}
|
|
84
|
+
};
|
|
85
|
+
var children = getChildrenWithNestedItems(currentSectionItemsSorted);
|
|
49
86
|
var SectionComponent = section.component || fallbacks.section || NoOp;
|
|
50
87
|
return /*#__PURE__*/_react.default.createElement(SectionComponent, {
|
|
51
88
|
key: section.key
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createBlockMenuRegistry } from './editor-actions';
|
|
3
|
+
import { formatNode } from './editor-commands/formatNode';
|
|
3
4
|
import { createPlugin } from './pm-plugins/main';
|
|
4
5
|
import BlockMenu from './ui/block-menu';
|
|
5
6
|
import { getBlockMenuComponents } from './ui/block-menu-components';
|
|
@@ -28,6 +29,11 @@ export const blockMenuPlugin = ({
|
|
|
28
29
|
return registry.components;
|
|
29
30
|
}
|
|
30
31
|
},
|
|
32
|
+
commands: {
|
|
33
|
+
formatNode: (currentNode, targetType) => {
|
|
34
|
+
return formatNode(currentNode, targetType);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
31
37
|
contentComponent({
|
|
32
38
|
editorView,
|
|
33
39
|
popupsMountPoint,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { transformNodeToTargetType } from './transforms/transformNodeToTargetType';
|
|
2
|
+
/**
|
|
3
|
+
* Formats the current node or selection to the specified target type
|
|
4
|
+
* @param currentNode - The current node
|
|
5
|
+
* @param targetType - The target node type to convert to
|
|
6
|
+
*/
|
|
7
|
+
export const formatNode = (currentNode, targetType) => {
|
|
8
|
+
return ({
|
|
9
|
+
tr
|
|
10
|
+
}) => {
|
|
11
|
+
const {
|
|
12
|
+
selection
|
|
13
|
+
} = tr;
|
|
14
|
+
try {
|
|
15
|
+
return transformNodeToTargetType(tr, currentNode, selection.from, targetType);
|
|
16
|
+
} catch (e) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { transformToContainer } from './container-transforms';
|
|
2
|
+
import { transformToList } from './list-transforms';
|
|
3
|
+
import { isListNodeType, isContainerNodeType, isBlockNodeType } from './utils';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Transform block nodes (paragraph, heading, codeblock)
|
|
7
|
+
*/
|
|
8
|
+
export const transformBlockNode = context => {
|
|
9
|
+
const {
|
|
10
|
+
tr,
|
|
11
|
+
targetNodeType,
|
|
12
|
+
targetAttrs
|
|
13
|
+
} = context;
|
|
14
|
+
const {
|
|
15
|
+
selection
|
|
16
|
+
} = tr;
|
|
17
|
+
const {
|
|
18
|
+
$from,
|
|
19
|
+
$to
|
|
20
|
+
} = selection;
|
|
21
|
+
|
|
22
|
+
// Handle transformation to list types
|
|
23
|
+
if (isListNodeType(targetNodeType)) {
|
|
24
|
+
return transformToList();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Handle transformation to container types (panel, expand, blockquote)
|
|
28
|
+
if (isContainerNodeType(targetNodeType)) {
|
|
29
|
+
return transformToContainer();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Handle block type transformation (paragraph, heading, codeblock)
|
|
33
|
+
if (isBlockNodeType(targetNodeType)) {
|
|
34
|
+
tr.setBlockType($from.pos, $to.pos, targetNodeType, targetAttrs);
|
|
35
|
+
return tr;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { isBlockNodeType, isListNodeType, isContainerNodeType } from './utils';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Transform selection to container type
|
|
5
|
+
*/
|
|
6
|
+
export const transformToContainer = () => {
|
|
7
|
+
return null;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Transform container nodes (panel, expand, blockquote)
|
|
12
|
+
*/
|
|
13
|
+
export const transformContainerNode = ({
|
|
14
|
+
tr,
|
|
15
|
+
sourcePos,
|
|
16
|
+
targetNodeType,
|
|
17
|
+
targetAttrs
|
|
18
|
+
}) => {
|
|
19
|
+
if (sourcePos === null) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Transform container to block type - unwrap and convert content
|
|
24
|
+
if (isBlockNodeType(targetNodeType)) {
|
|
25
|
+
return unwrapAndConvertToBlockType();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Transform container to list type
|
|
29
|
+
if (isListNodeType(targetNodeType)) {
|
|
30
|
+
return unwrapAndConvertToList();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Transform between container types
|
|
34
|
+
if (isContainerNodeType(targetNodeType)) {
|
|
35
|
+
tr.setNodeMarkup(sourcePos, targetNodeType, targetAttrs);
|
|
36
|
+
return tr;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Unwrap container node and convert content to block type
|
|
43
|
+
*/
|
|
44
|
+
export const unwrapAndConvertToBlockType = () => {
|
|
45
|
+
// Convert to block type directly
|
|
46
|
+
return null;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Unwrap container node and convert content to list
|
|
51
|
+
*/
|
|
52
|
+
export const unwrapAndConvertToList = () => {
|
|
53
|
+
// Convert to list directly
|
|
54
|
+
return null;
|
|
55
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { isBlockNodeType, isContainerNodeType, isListNodeType } from './utils';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Transform selection to list type
|
|
5
|
+
*/
|
|
6
|
+
export const transformToList = () => {
|
|
7
|
+
return null;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Transform list nodes
|
|
12
|
+
*/
|
|
13
|
+
export const transformListNode = ({
|
|
14
|
+
targetNodeType
|
|
15
|
+
}) => {
|
|
16
|
+
// Transform list to block type
|
|
17
|
+
if (isBlockNodeType(targetNodeType)) {
|
|
18
|
+
// Lift list items out of the list and convert to target block type
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Transform list to container type
|
|
23
|
+
if (isContainerNodeType(targetNodeType)) {
|
|
24
|
+
// Lift list items out of the list and convert to container type
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Transform between list types
|
|
29
|
+
if (isListNodeType(targetNodeType)) {
|
|
30
|
+
// Lift list items out of the list and convert to the other list type
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Lift list content and convert to block type
|
|
38
|
+
*/
|
|
39
|
+
export const liftListToBlockType = () => {
|
|
40
|
+
// Convert to target block type directly
|
|
41
|
+
return null;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Transform between different list types
|
|
46
|
+
*/
|
|
47
|
+
export const transformBetweenListTypes = () => {
|
|
48
|
+
return null;
|
|
49
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { transformBlockNode } from './block-transforms';
|
|
2
|
+
import { transformContainerNode } from './container-transforms';
|
|
3
|
+
import { transformListNode } from './list-transforms';
|
|
4
|
+
import { getTargetNodeInfo, isBlockNode, isListNode, isContainerNode } from './utils';
|
|
5
|
+
export function transformNodeToTargetType(tr, sourceNode, sourcePos, targetType) {
|
|
6
|
+
const {
|
|
7
|
+
nodes
|
|
8
|
+
} = tr.doc.type.schema;
|
|
9
|
+
const targetNodeInfo = getTargetNodeInfo(targetType, nodes);
|
|
10
|
+
if (!targetNodeInfo) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const {
|
|
14
|
+
nodeType: targetNodeType,
|
|
15
|
+
attrs: targetAttrs
|
|
16
|
+
} = targetNodeInfo;
|
|
17
|
+
|
|
18
|
+
// Early return if trying to transform to the same type
|
|
19
|
+
if (sourceNode.type === targetNodeType) {
|
|
20
|
+
return tr; // No transformation needed
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Prepare transformation context
|
|
24
|
+
const transformationContext = {
|
|
25
|
+
tr,
|
|
26
|
+
sourceNode,
|
|
27
|
+
sourcePos,
|
|
28
|
+
targetNodeType,
|
|
29
|
+
targetAttrs
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Route to appropriate transformation strategy based on source node type
|
|
33
|
+
try {
|
|
34
|
+
if (isBlockNode(sourceNode)) {
|
|
35
|
+
return transformBlockNode(transformationContext);
|
|
36
|
+
}
|
|
37
|
+
if (isListNode(sourceNode)) {
|
|
38
|
+
return transformListNode(transformationContext);
|
|
39
|
+
}
|
|
40
|
+
if (isContainerNode(sourceNode)) {
|
|
41
|
+
return transformContainerNode(transformationContext);
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
} catch (e) {
|
|
45
|
+
// Node transformation failed
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export const getTargetNodeInfo = (targetType, nodes) => {
|
|
2
|
+
switch (targetType) {
|
|
3
|
+
case 'heading1':
|
|
4
|
+
return {
|
|
5
|
+
nodeType: nodes.heading,
|
|
6
|
+
attrs: {
|
|
7
|
+
level: 1
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
case 'heading2':
|
|
11
|
+
return {
|
|
12
|
+
nodeType: nodes.heading,
|
|
13
|
+
attrs: {
|
|
14
|
+
level: 2
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
case 'heading3':
|
|
18
|
+
return {
|
|
19
|
+
nodeType: nodes.heading,
|
|
20
|
+
attrs: {
|
|
21
|
+
level: 3
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
case 'heading4':
|
|
25
|
+
return {
|
|
26
|
+
nodeType: nodes.heading,
|
|
27
|
+
attrs: {
|
|
28
|
+
level: 4
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
case 'heading5':
|
|
32
|
+
return {
|
|
33
|
+
nodeType: nodes.heading,
|
|
34
|
+
attrs: {
|
|
35
|
+
level: 5
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
case 'heading6':
|
|
39
|
+
return {
|
|
40
|
+
nodeType: nodes.heading,
|
|
41
|
+
attrs: {
|
|
42
|
+
level: 6
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
case 'paragraph':
|
|
46
|
+
return {
|
|
47
|
+
nodeType: nodes.paragraph
|
|
48
|
+
};
|
|
49
|
+
case 'blockquote':
|
|
50
|
+
return {
|
|
51
|
+
nodeType: nodes.blockquote
|
|
52
|
+
};
|
|
53
|
+
case 'expand':
|
|
54
|
+
return {
|
|
55
|
+
nodeType: nodes.expand
|
|
56
|
+
};
|
|
57
|
+
case 'panel':
|
|
58
|
+
return {
|
|
59
|
+
nodeType: nodes.panel,
|
|
60
|
+
attrs: {
|
|
61
|
+
panelType: 'info'
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
case 'codeblock':
|
|
65
|
+
return {
|
|
66
|
+
nodeType: nodes.codeBlock
|
|
67
|
+
};
|
|
68
|
+
case 'bulletList':
|
|
69
|
+
return {
|
|
70
|
+
nodeType: nodes.bulletList
|
|
71
|
+
};
|
|
72
|
+
case 'orderedList':
|
|
73
|
+
return {
|
|
74
|
+
nodeType: nodes.orderedList
|
|
75
|
+
};
|
|
76
|
+
case 'taskList':
|
|
77
|
+
return {
|
|
78
|
+
nodeType: nodes.taskList
|
|
79
|
+
};
|
|
80
|
+
default:
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Helper functions to categorize node types
|
|
86
|
+
export const isBlockNode = node => {
|
|
87
|
+
return ['paragraph', 'heading', 'codeBlock'].includes(node.type.name);
|
|
88
|
+
};
|
|
89
|
+
export const isListNode = node => {
|
|
90
|
+
return ['bulletList', 'orderedList', 'taskList', 'listItem'].includes(node.type.name);
|
|
91
|
+
};
|
|
92
|
+
export const isContainerNode = node => {
|
|
93
|
+
return ['panel', 'expand', 'blockquote'].includes(node.type.name);
|
|
94
|
+
};
|
|
95
|
+
export const isBlockNodeType = nodeType => {
|
|
96
|
+
return ['paragraph', 'heading', 'codeBlock'].includes(nodeType.name);
|
|
97
|
+
};
|
|
98
|
+
export const isListNodeType = nodeType => {
|
|
99
|
+
return ['bulletList', 'orderedList', 'taskList'].includes(nodeType.name);
|
|
100
|
+
};
|
|
101
|
+
export const isContainerNodeType = nodeType => {
|
|
102
|
+
return ['panel', 'expand', 'blockquote'].includes(nodeType.name);
|
|
103
|
+
};
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { ToolbarDropdownItemSection, ToolbarNestedDropdownMenu } from '@atlaskit/editor-toolbar';
|
|
3
3
|
import ChangesIcon from '@atlaskit/icon/core/changes';
|
|
4
4
|
import ChevronRightIcon from '@atlaskit/icon/core/chevron-right';
|
|
5
|
-
import
|
|
6
|
-
import TaskIcon from '@atlaskit/icon/core/task';
|
|
5
|
+
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';
|
|
@@ -34,13 +33,54 @@ const getMoveUpMoveDownMenuComponents = api => {
|
|
|
34
33
|
})
|
|
35
34
|
}];
|
|
36
35
|
};
|
|
36
|
+
const getFormatMenuComponents = () => {
|
|
37
|
+
return [{
|
|
38
|
+
type: 'block-menu-nested',
|
|
39
|
+
key: 'nested-menu-format',
|
|
40
|
+
parent: {
|
|
41
|
+
type: 'block-menu-section',
|
|
42
|
+
key: 'block-menu-section-primary',
|
|
43
|
+
rank: 100
|
|
44
|
+
},
|
|
45
|
+
component: ({
|
|
46
|
+
children
|
|
47
|
+
} = {
|
|
48
|
+
children: null
|
|
49
|
+
}) => {
|
|
50
|
+
return /*#__PURE__*/React.createElement(ToolbarNestedDropdownMenu, {
|
|
51
|
+
text: "Format",
|
|
52
|
+
elemBefore: /*#__PURE__*/React.createElement(ChangesIcon, {
|
|
53
|
+
label: ""
|
|
54
|
+
}),
|
|
55
|
+
elemAfter: /*#__PURE__*/React.createElement(ChevronRightIcon, {
|
|
56
|
+
label: 'example nested menu'
|
|
57
|
+
})
|
|
58
|
+
}, children);
|
|
59
|
+
}
|
|
60
|
+
}, {
|
|
61
|
+
type: 'block-menu-section',
|
|
62
|
+
key: 'nested-menu-format-section-primary',
|
|
63
|
+
parent: {
|
|
64
|
+
type: 'block-menu-nested',
|
|
65
|
+
key: 'nested-menu-format',
|
|
66
|
+
rank: 100
|
|
67
|
+
},
|
|
68
|
+
component: ({
|
|
69
|
+
children
|
|
70
|
+
} = {
|
|
71
|
+
children: null
|
|
72
|
+
}) => {
|
|
73
|
+
return /*#__PURE__*/React.createElement(ToolbarDropdownItemSection, null, children);
|
|
74
|
+
}
|
|
75
|
+
}];
|
|
76
|
+
};
|
|
37
77
|
export const getBlockMenuComponents = ({
|
|
38
78
|
api,
|
|
39
79
|
config
|
|
40
80
|
}) => {
|
|
41
|
-
return [{
|
|
81
|
+
return [...(fg('platform_editor_block_menu_format') ? getFormatMenuComponents() : []), {
|
|
42
82
|
type: 'block-menu-section',
|
|
43
|
-
key: 'block-menu-section-
|
|
83
|
+
key: 'block-menu-section-primary',
|
|
44
84
|
rank: 100,
|
|
45
85
|
component: ({
|
|
46
86
|
children
|
|
@@ -105,33 +145,6 @@ export const getBlockMenuComponents = ({
|
|
|
105
145
|
hasSeparator: true
|
|
106
146
|
}, children);
|
|
107
147
|
}
|
|
108
|
-
}, {
|
|
109
|
-
type: 'block-menu-nested',
|
|
110
|
-
key: 'nested-menu',
|
|
111
|
-
parent: {
|
|
112
|
-
type: 'block-menu-section',
|
|
113
|
-
key: 'block-menu-section-format',
|
|
114
|
-
rank: 100
|
|
115
|
-
},
|
|
116
|
-
component: () => {
|
|
117
|
-
return /*#__PURE__*/React.createElement(ToolbarNestedDropdownMenu, {
|
|
118
|
-
text: "Format",
|
|
119
|
-
elemBefore: /*#__PURE__*/React.createElement(ChangesIcon, {
|
|
120
|
-
label: ""
|
|
121
|
-
}),
|
|
122
|
-
elemAfter: /*#__PURE__*/React.createElement(ChevronRightIcon, {
|
|
123
|
-
label: 'example nested menu'
|
|
124
|
-
})
|
|
125
|
-
}, /*#__PURE__*/React.createElement(ToolbarDropdownItemSection, null, /*#__PURE__*/React.createElement(ToolbarDropdownItem, {
|
|
126
|
-
elemBefore: /*#__PURE__*/React.createElement(TaskIcon, {
|
|
127
|
-
label: ""
|
|
128
|
-
})
|
|
129
|
-
}, "Action item"), /*#__PURE__*/React.createElement(ToolbarDropdownItem, {
|
|
130
|
-
elemBefore: /*#__PURE__*/React.createElement(ListBulletedIcon, {
|
|
131
|
-
label: ""
|
|
132
|
-
})
|
|
133
|
-
}, "Bullet list")));
|
|
134
|
-
}
|
|
135
148
|
}, ...getMoveUpMoveDownMenuComponents(api), {
|
|
136
149
|
type: 'block-menu-item',
|
|
137
150
|
key: 'block-menu-item-delete',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { Fragment } from 'react';
|
|
2
2
|
const NoOp = props => null;
|
|
3
|
-
const
|
|
4
|
-
return component.type === 'block-menu-section';
|
|
3
|
+
const isNonNestedMenuSection = component => {
|
|
4
|
+
return component.type === 'block-menu-section' && !('parent' in component);
|
|
5
5
|
};
|
|
6
6
|
const isMenuItem = component => {
|
|
7
7
|
return component.type === 'block-menu-item';
|
|
@@ -9,24 +9,59 @@ const isMenuItem = component => {
|
|
|
9
9
|
const isNestedMenu = component => {
|
|
10
10
|
return component.type === 'block-menu-nested';
|
|
11
11
|
};
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
const isNestedMenuSection = component => {
|
|
13
|
+
return 'parent' in component && component.parent !== undefined && component.parent.type === 'block-menu-nested';
|
|
14
|
+
};
|
|
15
|
+
const getSortedChildren = (components, parentKey) => components.filter(component => 'parent' in component && component.parent !== undefined && component.parent.key === parentKey).sort((a, b) => (a.parent.rank || 0) - (b.parent.rank || 0));
|
|
16
|
+
const getSortedNestedSections = (components, parentKey) => {
|
|
17
|
+
const nestedMenuSections = components.filter(isNestedMenuSection);
|
|
18
|
+
const nestedMenuSectionsWithParent = nestedMenuSections.filter(section => section.parent !== undefined);
|
|
19
|
+
return getSortedChildren(nestedMenuSectionsWithParent, parentKey);
|
|
20
|
+
};
|
|
21
|
+
const getSortedNonNestedSections = components => {
|
|
22
|
+
return components.filter(isNonNestedMenuSection).sort((a, b) => (a.rank || 0) - (b.rank || 0));
|
|
15
23
|
};
|
|
16
24
|
export const BlockMenuRenderer = ({
|
|
17
25
|
components,
|
|
18
26
|
fallbacks
|
|
19
27
|
}) => {
|
|
20
|
-
const menuSections =
|
|
28
|
+
const menuSections = getSortedNonNestedSections(components);
|
|
21
29
|
const menuItems = components.filter(isMenuItem);
|
|
22
30
|
const nestedMenus = components.filter(isNestedMenu);
|
|
23
31
|
return /*#__PURE__*/React.createElement(Fragment, null, menuSections.map(section => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
// Get all items for the current section, including nested menus, and sort them by rank
|
|
33
|
+
const currentSectionItemsSorted = getSortedChildren([...menuItems, ...nestedMenus], section.key);
|
|
34
|
+
|
|
35
|
+
// iterate over the current section items, if it is nested menu, get their children, sort them
|
|
36
|
+
// if they are menu items, just render as they are sorted above
|
|
37
|
+
const getChildrenWithNestedItems = items => {
|
|
38
|
+
return items.map(item => {
|
|
39
|
+
if (isNestedMenu(item)) {
|
|
40
|
+
const sortedNestedSections = getSortedNestedSections(components, item.key);
|
|
41
|
+
return sortedNestedSections.map(section => {
|
|
42
|
+
const sortedNestedMenuItems = getSortedChildren(menuItems, section.key);
|
|
43
|
+
const NestedMenuComponent = item.component || fallbacks.nestedMenu || NoOp;
|
|
44
|
+
const NestedSection = section.component || fallbacks.section || NoOp;
|
|
45
|
+
return /*#__PURE__*/React.createElement(NestedMenuComponent, {
|
|
46
|
+
key: item.key
|
|
47
|
+
}, /*#__PURE__*/React.createElement(NestedSection, {
|
|
48
|
+
key: section.key
|
|
49
|
+
}, sortedNestedMenuItems.map(nestedItem => {
|
|
50
|
+
const NestedMenuItemComponent = nestedItem.component || fallbacks.item || NoOp;
|
|
51
|
+
return /*#__PURE__*/React.createElement(NestedMenuItemComponent, {
|
|
52
|
+
key: nestedItem.key
|
|
53
|
+
});
|
|
54
|
+
})));
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
const ItemComponent = item.component || fallbacks.item || NoOp;
|
|
58
|
+
return /*#__PURE__*/React.createElement(ItemComponent, {
|
|
59
|
+
key: item.key
|
|
60
|
+
});
|
|
61
|
+
}
|
|
28
62
|
});
|
|
29
|
-
}
|
|
63
|
+
};
|
|
64
|
+
const children = getChildrenWithNestedItems(currentSectionItemsSorted);
|
|
30
65
|
const SectionComponent = section.component || fallbacks.section || NoOp;
|
|
31
66
|
return /*#__PURE__*/React.createElement(SectionComponent, {
|
|
32
67
|
key: section.key
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createBlockMenuRegistry } from './editor-actions';
|
|
3
|
+
import { formatNode as _formatNode } from './editor-commands/formatNode';
|
|
3
4
|
import { createPlugin } from './pm-plugins/main';
|
|
4
5
|
import BlockMenu from './ui/block-menu';
|
|
5
6
|
import { getBlockMenuComponents } from './ui/block-menu-components';
|
|
@@ -27,6 +28,11 @@ export var blockMenuPlugin = function blockMenuPlugin(_ref) {
|
|
|
27
28
|
return registry.components;
|
|
28
29
|
}
|
|
29
30
|
},
|
|
31
|
+
commands: {
|
|
32
|
+
formatNode: function formatNode(currentNode, targetType) {
|
|
33
|
+
return _formatNode(currentNode, targetType);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
30
36
|
contentComponent: function contentComponent(_ref2) {
|
|
31
37
|
var editorView = _ref2.editorView,
|
|
32
38
|
popupsMountPoint = _ref2.popupsMountPoint,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { transformNodeToTargetType } from './transforms/transformNodeToTargetType';
|
|
2
|
+
/**
|
|
3
|
+
* Formats the current node or selection to the specified target type
|
|
4
|
+
* @param currentNode - The current node
|
|
5
|
+
* @param targetType - The target node type to convert to
|
|
6
|
+
*/
|
|
7
|
+
export var formatNode = function formatNode(currentNode, targetType) {
|
|
8
|
+
return function (_ref) {
|
|
9
|
+
var tr = _ref.tr;
|
|
10
|
+
var selection = tr.selection;
|
|
11
|
+
try {
|
|
12
|
+
return transformNodeToTargetType(tr, currentNode, selection.from, targetType);
|
|
13
|
+
} catch (e) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { transformToContainer } from './container-transforms';
|
|
2
|
+
import { transformToList } from './list-transforms';
|
|
3
|
+
import { isListNodeType, isContainerNodeType, isBlockNodeType } from './utils';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Transform block nodes (paragraph, heading, codeblock)
|
|
7
|
+
*/
|
|
8
|
+
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;
|
|
15
|
+
|
|
16
|
+
// Handle transformation to list types
|
|
17
|
+
if (isListNodeType(targetNodeType)) {
|
|
18
|
+
return transformToList();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Handle transformation to container types (panel, expand, blockquote)
|
|
22
|
+
if (isContainerNodeType(targetNodeType)) {
|
|
23
|
+
return transformToContainer();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Handle block type transformation (paragraph, heading, codeblock)
|
|
27
|
+
if (isBlockNodeType(targetNodeType)) {
|
|
28
|
+
tr.setBlockType($from.pos, $to.pos, targetNodeType, targetAttrs);
|
|
29
|
+
return tr;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
};
|