@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.
Files changed (55) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/afm-cc/tsconfig.json +4 -1
  3. package/afm-dev-agents/tsconfig.json +3 -0
  4. package/afm-jira/tsconfig.json +3 -0
  5. package/afm-passionfruit/tsconfig.json +3 -0
  6. package/afm-post-office/tsconfig.json +3 -0
  7. package/afm-rovo-extension/tsconfig.json +3 -0
  8. package/afm-townsquare/tsconfig.json +3 -0
  9. package/dist/cjs/blockMenuPlugin.js +6 -0
  10. package/dist/cjs/editor-commands/formatNode.js +23 -0
  11. package/dist/cjs/editor-commands/transforms/block-transforms.js +37 -0
  12. package/dist/cjs/editor-commands/transforms/container-transforms.js +59 -0
  13. package/dist/cjs/editor-commands/transforms/list-transforms.js +53 -0
  14. package/dist/cjs/editor-commands/transforms/transformNodeToTargetType.js +50 -0
  15. package/dist/cjs/editor-commands/transforms/types.js +5 -0
  16. package/dist/cjs/editor-commands/transforms/utils.js +109 -0
  17. package/dist/cjs/ui/block-menu-components.js +55 -42
  18. package/dist/cjs/ui/block-menu-renderer.js +48 -11
  19. package/dist/es2019/blockMenuPlugin.js +6 -0
  20. package/dist/es2019/editor-commands/formatNode.js +20 -0
  21. package/dist/es2019/editor-commands/transforms/block-transforms.js +38 -0
  22. package/dist/es2019/editor-commands/transforms/container-transforms.js +55 -0
  23. package/dist/es2019/editor-commands/transforms/list-transforms.js +49 -0
  24. package/dist/es2019/editor-commands/transforms/transformNodeToTargetType.js +48 -0
  25. package/dist/es2019/editor-commands/transforms/types.js +1 -0
  26. package/dist/es2019/editor-commands/transforms/utils.js +103 -0
  27. package/dist/es2019/ui/block-menu-components.js +45 -32
  28. package/dist/es2019/ui/block-menu-renderer.js +46 -11
  29. package/dist/esm/blockMenuPlugin.js +6 -0
  30. package/dist/esm/editor-commands/formatNode.js +17 -0
  31. package/dist/esm/editor-commands/transforms/block-transforms.js +32 -0
  32. package/dist/esm/editor-commands/transforms/container-transforms.js +54 -0
  33. package/dist/esm/editor-commands/transforms/list-transforms.js +48 -0
  34. package/dist/esm/editor-commands/transforms/transformNodeToTargetType.js +44 -0
  35. package/dist/esm/editor-commands/transforms/types.js +1 -0
  36. package/dist/esm/editor-commands/transforms/utils.js +103 -0
  37. package/dist/esm/ui/block-menu-components.js +56 -43
  38. package/dist/esm/ui/block-menu-renderer.js +48 -11
  39. package/dist/types/blockMenuPluginType.d.ts +27 -3
  40. package/dist/types/editor-commands/formatNode.d.ts +9 -0
  41. package/dist/types/editor-commands/transforms/block-transforms.d.ts +5 -0
  42. package/dist/types/editor-commands/transforms/container-transforms.d.ts +17 -0
  43. package/dist/types/editor-commands/transforms/list-transforms.d.ts +17 -0
  44. package/dist/types/editor-commands/transforms/transformNodeToTargetType.d.ts +4 -0
  45. package/dist/types/editor-commands/transforms/types.d.ts +11 -0
  46. package/dist/types/editor-commands/transforms/utils.d.ts +12 -0
  47. package/dist/types-ts4.5/blockMenuPluginType.d.ts +27 -3
  48. package/dist/types-ts4.5/editor-commands/formatNode.d.ts +9 -0
  49. package/dist/types-ts4.5/editor-commands/transforms/block-transforms.d.ts +5 -0
  50. package/dist/types-ts4.5/editor-commands/transforms/container-transforms.d.ts +17 -0
  51. package/dist/types-ts4.5/editor-commands/transforms/list-transforms.d.ts +17 -0
  52. package/dist/types-ts4.5/editor-commands/transforms/transformNodeToTargetType.d.ts +4 -0
  53. package/dist/types-ts4.5/editor-commands/transforms/types.d.ts +11 -0
  54. package/dist/types-ts4.5/editor-commands/transforms/utils.d.ts +12 -0
  55. 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 isMenuSection = function isMenuSection(component) {
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 getSortedSections = function getSortedSections(components) {
32
- return components.filter(isMenuSection).sort(function (a, b) {
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 = getSortedSections(components);
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
- var children = getSortedChildren([].concat((0, _toConsumableArray2.default)(menuItems), (0, _toConsumableArray2.default)(nestedMenus)), section.key).map(function (item) {
44
- var ItemComponent = item.component || fallbacks.item || NoOp;
45
- return /*#__PURE__*/_react.default.createElement(ItemComponent, {
46
- key: item.key
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 { ToolbarDropdownItem, ToolbarDropdownItemSection, ToolbarNestedDropdownMenu } from '@atlaskit/editor-toolbar';
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 ListBulletedIcon from '@atlaskit/icon/core/list-bulleted';
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-format',
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 isMenuSection = component => {
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 getSortedChildren = (components, parentKey) => components.filter(component => component.parent.key === parentKey).sort((a, b) => (a.parent.rank || 0) - (b.parent.rank || 0));
13
- const getSortedSections = components => {
14
- return components.filter(isMenuSection).sort((a, b) => (a.rank || 0) - (b.rank || 0));
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 = getSortedSections(components);
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
- const children = getSortedChildren([...menuItems, ...nestedMenus], section.key).map(item => {
25
- const ItemComponent = item.component || fallbacks.item || NoOp;
26
- return /*#__PURE__*/React.createElement(ItemComponent, {
27
- key: item.key
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
+ };