@atlaskit/editor-plugin-block-menu 6.0.4 → 6.0.6
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/dist/cjs/editor-commands/transform-node-utils/steps/convertEachNodeStep.js +46 -0
- package/dist/cjs/editor-commands/transform-node-utils/steps/mergeNeighbourListsStep.js +91 -0
- package/dist/cjs/editor-commands/transform-node-utils/steps/wrapBlockquoteToDecisionListStep.js +2 -12
- package/dist/cjs/editor-commands/transform-node-utils/steps/wrapMixedContentStep.js +69 -52
- package/dist/cjs/editor-commands/transform-node-utils/transform.js +45 -30
- package/dist/cjs/editor-commands/transform-node-utils/utils.js +10 -1
- package/dist/cjs/editor-commands/transformNode.js +65 -65
- package/dist/es2019/editor-commands/transform-node-utils/steps/convertEachNodeStep.js +28 -0
- package/dist/es2019/editor-commands/transform-node-utils/steps/mergeNeighbourListsStep.js +75 -0
- package/dist/es2019/editor-commands/transform-node-utils/steps/wrapBlockquoteToDecisionListStep.js +1 -11
- package/dist/es2019/editor-commands/transform-node-utils/steps/wrapMixedContentStep.js +71 -53
- package/dist/es2019/editor-commands/transform-node-utils/transform.js +44 -29
- package/dist/es2019/editor-commands/transform-node-utils/utils.js +10 -0
- package/dist/es2019/editor-commands/transformNode.js +64 -67
- package/dist/esm/editor-commands/transform-node-utils/steps/convertEachNodeStep.js +39 -0
- package/dist/esm/editor-commands/transform-node-utils/steps/mergeNeighbourListsStep.js +85 -0
- package/dist/esm/editor-commands/transform-node-utils/steps/wrapBlockquoteToDecisionListStep.js +1 -11
- package/dist/esm/editor-commands/transform-node-utils/steps/wrapMixedContentStep.js +70 -53
- package/dist/esm/editor-commands/transform-node-utils/transform.js +44 -29
- package/dist/esm/editor-commands/transform-node-utils/utils.js +10 -0
- package/dist/esm/editor-commands/transformNode.js +64 -65
- package/dist/types/editor-commands/transform-node-utils/steps/convertEachNodeStep.d.ts +2 -0
- package/dist/types/editor-commands/transform-node-utils/steps/mergeNeighbourListsStep.d.ts +32 -0
- package/dist/types/editor-commands/transform-node-utils/steps/wrapMixedContentStep.d.ts +17 -2
- package/dist/types/editor-commands/transform-node-utils/transform.d.ts +16 -1
- package/dist/types/editor-commands/transform-node-utils/utils.d.ts +5 -0
- package/dist/types/editor-commands/transformNode.d.ts +1 -1
- package/dist/types-ts4.5/editor-commands/transform-node-utils/steps/convertEachNodeStep.d.ts +2 -0
- package/dist/types-ts4.5/editor-commands/transform-node-utils/steps/mergeNeighbourListsStep.d.ts +32 -0
- package/dist/types-ts4.5/editor-commands/transform-node-utils/steps/wrapMixedContentStep.d.ts +17 -2
- package/dist/types-ts4.5/editor-commands/transform-node-utils/transform.d.ts +16 -1
- package/dist/types-ts4.5/editor-commands/transform-node-utils/utils.d.ts +5 -0
- package/dist/types-ts4.5/editor-commands/transformNode.d.ts +1 -1
- package/package.json +4 -4
- package/dist/cjs/editor-commands/transform-node-utils/tranformContent.js +0 -19
- package/dist/cjs/editor-commands/transform-node-utils/wrapIntoLayoutStep.js +0 -20
- package/dist/es2019/editor-commands/transform-node-utils/tranformContent.js +0 -13
- package/dist/es2019/editor-commands/transform-node-utils/wrapIntoLayoutStep.js +0 -17
- package/dist/esm/editor-commands/transform-node-utils/tranformContent.js +0 -13
- package/dist/esm/editor-commands/transform-node-utils/wrapIntoLayoutStep.js +0 -14
- package/dist/types/editor-commands/transform-node-utils/tranformContent.d.ts +0 -3
- package/dist/types/editor-commands/transform-node-utils/wrapIntoLayoutStep.d.ts +0 -2
- package/dist/types-ts4.5/editor-commands/transform-node-utils/tranformContent.d.ts +0 -3
- package/dist/types-ts4.5/editor-commands/transform-node-utils/wrapIntoLayoutStep.d.ts +0 -2
|
@@ -1,80 +1,80 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
3
4
|
Object.defineProperty(exports, "__esModule", {
|
|
4
5
|
value: true
|
|
5
6
|
});
|
|
6
7
|
exports.transformNode = void 0;
|
|
8
|
+
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
7
9
|
var _analytics = require("@atlaskit/editor-common/analytics");
|
|
8
10
|
var _performanceMeasures = require("@atlaskit/editor-common/performance-measures");
|
|
9
11
|
var _selection = require("@atlaskit/editor-common/selection");
|
|
10
12
|
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
11
13
|
var _isNestedNode = require("../ui/utils/isNestedNode");
|
|
12
|
-
var
|
|
14
|
+
var _transform = require("./transform-node-utils/transform");
|
|
13
15
|
var _utils = require("./transforms/utils");
|
|
14
16
|
var transformNode = exports.transformNode = function transformNode(api) {
|
|
15
|
-
return (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
var preservedSelection = api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 || (_api$blockControls = _api$blockControls.sharedState.currentState()) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.preservedSelection;
|
|
22
|
-
if (!preservedSelection) {
|
|
23
|
-
return tr;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Start performance measurement
|
|
27
|
-
var measureId = "transformNode_".concat(targetType.name, "_").concat(Date.now());
|
|
28
|
-
(0, _performanceMeasures.startMeasure)(measureId);
|
|
29
|
-
var schema = tr.doc.type.schema;
|
|
30
|
-
var nodes = schema.nodes;
|
|
31
|
-
var _expandSelectionToBlo = (0, _selection.expandSelectionToBlockRange)(preservedSelection),
|
|
32
|
-
$from = _expandSelectionToBlo.$from,
|
|
33
|
-
$to = _expandSelectionToBlo.$to;
|
|
34
|
-
var selectedParent = $from.parent;
|
|
35
|
-
var isNested = (0, _isNestedNode.isNestedNode)(preservedSelection, '');
|
|
36
|
-
var isList = (0, _utils.isListNode)(selectedParent);
|
|
37
|
-
var slice = tr.doc.slice(isList ? $from.pos - 1 : $from.pos, isList ? $to.pos + 1 : $to.pos);
|
|
38
|
-
|
|
39
|
-
// Collect source node information for analytics before transformation
|
|
40
|
-
var nodeCount = 0;
|
|
41
|
-
var sourceNodeTypes = {};
|
|
42
|
-
slice.content.forEach(function (node) {
|
|
43
|
-
nodeCount++;
|
|
44
|
-
var nodeTypeName = node.type.name;
|
|
45
|
-
sourceNodeTypes[nodeTypeName] = (sourceNodeTypes[nodeTypeName] || 0) + 1;
|
|
46
|
-
});
|
|
47
|
-
var transformedNodes = (0, _tranformContent.tranformContent)(slice.content, targetType, schema, isNested, metadata === null || metadata === void 0 ? void 0 : metadata.targetAttrs, selectedParent);
|
|
48
|
-
var nodesToDeleteAndInsert = [nodes.mediaSingle];
|
|
49
|
-
if (preservedSelection instanceof _state.NodeSelection && nodesToDeleteAndInsert.includes(preservedSelection.node.type)) {
|
|
50
|
-
// when node is media single, use tr.replaceWith freeze editor, if modify position, tr.replaceWith creates duplicats
|
|
51
|
-
tr.deleteRange($from.pos, $to.pos);
|
|
52
|
-
tr.insert($from.pos, transformedNodes);
|
|
53
|
-
} else {
|
|
54
|
-
tr.replaceWith(isList ? $from.pos - 1 : $from.pos, $to.pos, transformedNodes);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Stop performance measurement and fire analytics
|
|
58
|
-
(0, _performanceMeasures.stopMeasure)(measureId, function (duration, startTime) {
|
|
59
|
-
var _api$analytics;
|
|
60
|
-
api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 || _api$analytics.fireAnalyticsEvent({
|
|
61
|
-
action: _analytics.ACTION.TRANSFORMED,
|
|
62
|
-
actionSubject: _analytics.ACTION_SUBJECT.ELEMENT,
|
|
63
|
-
actionSubjectId: _analytics.ACTION_SUBJECT_ID.TRANSFORM,
|
|
64
|
-
attributes: {
|
|
65
|
-
duration: duration,
|
|
66
|
-
isList: isList,
|
|
67
|
-
isNested: isNested,
|
|
68
|
-
nodeCount: nodeCount,
|
|
69
|
-
sourceNodeTypes: sourceNodeTypes,
|
|
70
|
-
startTime: startTime,
|
|
71
|
-
targetNodeType: targetType.name
|
|
72
|
-
},
|
|
73
|
-
eventType: _analytics.EVENT_TYPE.OPERATIONAL
|
|
74
|
-
});
|
|
75
|
-
});
|
|
17
|
+
return function (targetType, metadata) {
|
|
18
|
+
return function (_ref) {
|
|
19
|
+
var _api$blockControls;
|
|
20
|
+
var tr = _ref.tr;
|
|
21
|
+
var preservedSelection = api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 || (_api$blockControls = _api$blockControls.sharedState.currentState()) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.preservedSelection;
|
|
22
|
+
if (!preservedSelection) {
|
|
76
23
|
return tr;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
24
|
+
}
|
|
25
|
+
var measureId = "transformNode_".concat(targetType.name, "_").concat(Date.now());
|
|
26
|
+
(0, _performanceMeasures.startMeasure)(measureId);
|
|
27
|
+
var nodes = tr.doc.type.schema.nodes;
|
|
28
|
+
var _expandSelectionToBlo = (0, _selection.expandSelectionToBlockRange)(preservedSelection),
|
|
29
|
+
$from = _expandSelectionToBlo.$from,
|
|
30
|
+
$to = _expandSelectionToBlo.$to;
|
|
31
|
+
var selectedParent = $from.parent;
|
|
32
|
+
var isParentLayout = selectedParent.type === nodes.layoutColumn;
|
|
33
|
+
var isNested = (0, _isNestedNode.isNestedNode)(preservedSelection, '') && !isParentLayout;
|
|
34
|
+
var isList = (0, _utils.isListNode)(selectedParent);
|
|
35
|
+
var sliceStart = isList ? $from.pos - 1 : $from.pos;
|
|
36
|
+
var sliceEnd = isList ? $to.pos + 1 : $to.pos;
|
|
37
|
+
var slice = tr.doc.slice(sliceStart, sliceEnd);
|
|
38
|
+
var sourceNodes = (0, _toConsumableArray2.default)(slice.content.content);
|
|
39
|
+
var sourceNodeTypes = {};
|
|
40
|
+
sourceNodes.forEach(function (node) {
|
|
41
|
+
var typeName = node.type.name;
|
|
42
|
+
sourceNodeTypes[typeName] = (sourceNodeTypes[typeName] || 0) + 1;
|
|
43
|
+
});
|
|
44
|
+
var resultNodes = (0, _transform.convertNodesToTargetType)({
|
|
45
|
+
sourceNodes: sourceNodes,
|
|
46
|
+
targetNodeType: targetType,
|
|
47
|
+
schema: tr.doc.type.schema,
|
|
48
|
+
isNested: isNested,
|
|
49
|
+
targetAttrs: metadata === null || metadata === void 0 ? void 0 : metadata.targetAttrs,
|
|
50
|
+
parentNode: selectedParent
|
|
51
|
+
});
|
|
52
|
+
var content = resultNodes.length > 0 ? resultNodes : slice.content;
|
|
53
|
+
if (preservedSelection instanceof _state.NodeSelection && preservedSelection.node.type === nodes.mediaSingle) {
|
|
54
|
+
tr.deleteRange($from.pos, $to.pos);
|
|
55
|
+
tr.insert($from.pos, content);
|
|
56
|
+
} else {
|
|
57
|
+
tr.replaceWith(sliceStart, $to.pos, content);
|
|
58
|
+
}
|
|
59
|
+
(0, _performanceMeasures.stopMeasure)(measureId, function (duration, startTime) {
|
|
60
|
+
var _api$analytics;
|
|
61
|
+
api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 || _api$analytics.fireAnalyticsEvent({
|
|
62
|
+
action: _analytics.ACTION.TRANSFORMED,
|
|
63
|
+
actionSubject: _analytics.ACTION_SUBJECT.ELEMENT,
|
|
64
|
+
actionSubjectId: _analytics.ACTION_SUBJECT_ID.TRANSFORM,
|
|
65
|
+
attributes: {
|
|
66
|
+
duration: duration,
|
|
67
|
+
isList: isList,
|
|
68
|
+
isNested: isNested,
|
|
69
|
+
nodeCount: sourceNodes.length,
|
|
70
|
+
sourceNodeTypes: sourceNodeTypes,
|
|
71
|
+
startTime: startTime,
|
|
72
|
+
targetNodeType: targetType.name
|
|
73
|
+
},
|
|
74
|
+
eventType: _analytics.EVENT_TYPE.OPERATIONAL
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
return tr;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
80
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { convertNodesToTargetType } from '../transform';
|
|
2
|
+
export const convertEachNodeStep = (nodes, context) => {
|
|
3
|
+
const {
|
|
4
|
+
schema,
|
|
5
|
+
targetNodeTypeName,
|
|
6
|
+
targetAttrs
|
|
7
|
+
} = context;
|
|
8
|
+
const targetNodeType = schema.nodes[targetNodeTypeName];
|
|
9
|
+
if (!targetNodeType) {
|
|
10
|
+
return nodes;
|
|
11
|
+
}
|
|
12
|
+
const resultNodes = [];
|
|
13
|
+
for (const node of nodes) {
|
|
14
|
+
const transformedNodes = convertNodesToTargetType({
|
|
15
|
+
sourceNodes: [node],
|
|
16
|
+
targetNodeType,
|
|
17
|
+
schema,
|
|
18
|
+
isNested: false,
|
|
19
|
+
targetAttrs
|
|
20
|
+
});
|
|
21
|
+
if (transformedNodes.length > 0) {
|
|
22
|
+
resultNodes.push(...transformedNodes);
|
|
23
|
+
} else {
|
|
24
|
+
resultNodes.push(node);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return resultNodes;
|
|
28
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { isListWithIndentation } from '../nodeChecks';
|
|
2
|
+
/**
|
|
3
|
+
* Merges consecutive lists of the same type into a single list.
|
|
4
|
+
* This step is useful after multiToListStep which may create multiple separate lists
|
|
5
|
+
* that should be combined.
|
|
6
|
+
*
|
|
7
|
+
* Handles both nestable lists (bulletList, orderedList, taskList) and
|
|
8
|
+
* non-nestable lists (decisionList).
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* Input:
|
|
12
|
+
* - bulletList
|
|
13
|
+
* - listItem "1"
|
|
14
|
+
* - bulletList
|
|
15
|
+
* - listItem "2"
|
|
16
|
+
* - panel (non-list node)
|
|
17
|
+
* - bulletList
|
|
18
|
+
* - listItem "3"
|
|
19
|
+
*
|
|
20
|
+
* Output:
|
|
21
|
+
* - bulletList
|
|
22
|
+
* - listItem "1"
|
|
23
|
+
* - listItem "2"
|
|
24
|
+
* - panel (non-list node)
|
|
25
|
+
* - bulletList
|
|
26
|
+
* - listItem "3"
|
|
27
|
+
*
|
|
28
|
+
* @param nodes - The nodes to process
|
|
29
|
+
* @param context - The transformation context
|
|
30
|
+
* @returns The merged nodes
|
|
31
|
+
*/
|
|
32
|
+
export const mergeNeighbourListsStep = (nodes, context) => {
|
|
33
|
+
if (nodes.length === 0) {
|
|
34
|
+
return nodes;
|
|
35
|
+
}
|
|
36
|
+
const {
|
|
37
|
+
schema
|
|
38
|
+
} = context;
|
|
39
|
+
const resultNodes = [];
|
|
40
|
+
let currentList = null;
|
|
41
|
+
|
|
42
|
+
// Check if a node is any type of list (including decisionList)
|
|
43
|
+
const isList = node => {
|
|
44
|
+
return isListWithIndentation(node.type.name, schema) || node.type.name === 'decisionList';
|
|
45
|
+
};
|
|
46
|
+
for (const node of nodes) {
|
|
47
|
+
// Check if this node is any type of list
|
|
48
|
+
if (isList(node)) {
|
|
49
|
+
if (currentList && currentList.type === node.type) {
|
|
50
|
+
// Merge with the current list by combining their children
|
|
51
|
+
const mergedContent = currentList.content.append(node.content);
|
|
52
|
+
currentList = currentList.type.create(currentList.attrs, mergedContent);
|
|
53
|
+
} else {
|
|
54
|
+
// Start a new list or switch to a different list type
|
|
55
|
+
if (currentList) {
|
|
56
|
+
resultNodes.push(currentList);
|
|
57
|
+
}
|
|
58
|
+
currentList = node;
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
// Non-list node - push any accumulated list and then this node
|
|
62
|
+
if (currentList) {
|
|
63
|
+
resultNodes.push(currentList);
|
|
64
|
+
currentList = null;
|
|
65
|
+
}
|
|
66
|
+
resultNodes.push(node);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Don't forget the last list if we ended with one
|
|
71
|
+
if (currentList) {
|
|
72
|
+
resultNodes.push(currentList);
|
|
73
|
+
}
|
|
74
|
+
return resultNodes;
|
|
75
|
+
};
|
package/dist/es2019/editor-commands/transform-node-utils/steps/wrapBlockquoteToDecisionListStep.js
CHANGED
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Determines if a node is a text node (heading or paragraph).
|
|
5
|
-
* Only text nodes should have their inline content extracted for decisionItem.
|
|
6
|
-
* All other nodes should break out.
|
|
7
|
-
*/
|
|
8
|
-
const isTextNode = node => {
|
|
9
|
-
const category = NODE_CATEGORY_BY_TYPE[node.type.name];
|
|
10
|
-
return category === 'text';
|
|
11
|
-
};
|
|
1
|
+
import { isTextNode } from '../utils';
|
|
12
2
|
|
|
13
3
|
/**
|
|
14
4
|
* Creates a decisionItem with the given inline content.
|
|
@@ -1,34 +1,7 @@
|
|
|
1
1
|
import { Fragment } from '@atlaskit/editor-prosemirror/model';
|
|
2
2
|
import { removeDisallowedMarks } from '../marks';
|
|
3
3
|
import { NODE_CATEGORY_BY_TYPE } from '../types';
|
|
4
|
-
import { convertTextNodeToParagraph } from '../utils';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Determines if a node is a text node (heading or paragraph).
|
|
8
|
-
* Text nodes can have their content converted to paragraphs when they can't be wrapped directly.
|
|
9
|
-
*/
|
|
10
|
-
const isTextNode = node => {
|
|
11
|
-
const category = NODE_CATEGORY_BY_TYPE[node.type.name];
|
|
12
|
-
return category === 'text';
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Determines if a node can be wrapped in the target container type, removes block marks from the node during check.
|
|
17
|
-
* Uses the schema's validContent to check if the target container can hold this node.
|
|
18
|
-
*
|
|
19
|
-
* Note: What can be wrapped depends on the target container type - for example:
|
|
20
|
-
* - Tables and media CAN go inside expand nodes
|
|
21
|
-
* - Tables CANNOT go inside panels or blockquotes
|
|
22
|
-
*/
|
|
23
|
-
const canWrapInTarget = (node, targetNodeType, targetNodeTypeName) => {
|
|
24
|
-
// Same-type containers should break out as separate containers
|
|
25
|
-
if (node.type.name === targetNodeTypeName) {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Use the schema to determine if this node can be contained in the target
|
|
30
|
-
return targetNodeType.validContent(Fragment.from(removeDisallowedMarks([node], targetNodeType)));
|
|
31
|
-
};
|
|
4
|
+
import { convertTextNodeToParagraph, isTextNode } from '../utils';
|
|
32
5
|
|
|
33
6
|
/**
|
|
34
7
|
* Handles the edge case where transforming from a container to another container results in
|
|
@@ -68,18 +41,33 @@ const handleEmptyContainerEdgeCase = (result, hasCreatedContainer, fromNode, tar
|
|
|
68
41
|
* - Same-type containers break out as separate containers (preserved as-is)
|
|
69
42
|
* - NestedExpands break out as regular expands (converted since nestedExpand can't exist outside expand)
|
|
70
43
|
* - Container structures that can't be nested in target break out (not flattened)
|
|
71
|
-
* - Text/list nodes that can't be wrapped are
|
|
44
|
+
* - Text/list nodes that can't be wrapped are converted to paragraphs and merged into the container
|
|
72
45
|
* - Atomic nodes (tables, media, macros) break out
|
|
73
46
|
*
|
|
47
|
+
* Special handling for layouts:
|
|
48
|
+
* - Layout sections break out as separate layouts (preserved as-is, not wrapped)
|
|
49
|
+
* - Other nodes (including headings, paragraphs, lists) are wrapped into layout columns within a layout section
|
|
50
|
+
* - Layouts always require layoutColumns as children (never paragraphs directly)
|
|
51
|
+
* - Layout columns can contain most block content including headings, paragraphs, lists, etc.
|
|
52
|
+
*
|
|
53
|
+
* Edge case handling:
|
|
54
|
+
* - For regular containers: If all content breaks out (container → container transform with no
|
|
55
|
+
* valid children), an empty container with a paragraph is created to ensure the target type exists
|
|
56
|
+
* - For layouts: Edge case handling is skipped because layouts require columns, not direct paragraphs.
|
|
57
|
+
* If all content breaks out, only the broken-out nodes are returned (no empty layout created)
|
|
58
|
+
*
|
|
74
59
|
* What can be wrapped depends on the target container's schema:
|
|
75
60
|
* - expand → panel: tables break out, nestedExpands convert to expands and break out
|
|
76
|
-
* - expand → blockquote: tables/media break out, nestedExpands convert to expands and break out
|
|
61
|
+
* - expand → blockquote: tables/media break out, nestedExpands convert to expands and break out, headings converted to paragraphs
|
|
77
62
|
* - expand → expand: tables/media stay inside (expands can contain them)
|
|
63
|
+
* - multi → layoutSection: layout sections break out, headings/paragraphs/lists wrapped into layout columns
|
|
78
64
|
*
|
|
79
65
|
* Example: expand(p('a'), table(), p('b')) → panel: [panel(p('a')), table(), panel(p('b'))]
|
|
80
66
|
* Example: expand(p('a'), panel(p('x')), p('b')) → panel: [panel(p('a')), panel(p('x')), panel(p('b'))]
|
|
81
67
|
* Example: expand(p('a'), nestedExpand({title: 'inner'})(p('x')), p('b')) → panel: [panel(p('a')), expand({title: 'inner'})(p('x')), panel(p('b'))]
|
|
82
68
|
* Example: expand(nestedExpand()(p())) → panel: [panel(), expand()(p())] (empty panel when all content breaks out)
|
|
69
|
+
* Example: [p('a'), layoutSection(...), p('b')] → layoutSection: [layoutSection(layoutColumn(p('a'))), layoutSection(...), layoutSection(layoutColumn(p('b')))]
|
|
70
|
+
* Example: [h1('heading'), p('para')] → layoutSection: [layoutSection(layoutColumn(h1('heading'), p('para')))] (headings stay as headings in layouts)
|
|
83
71
|
*/
|
|
84
72
|
export const wrapMixedContentStep = (nodes, context) => {
|
|
85
73
|
const {
|
|
@@ -91,48 +79,78 @@ export const wrapMixedContentStep = (nodes, context) => {
|
|
|
91
79
|
if (!targetNodeType) {
|
|
92
80
|
return nodes;
|
|
93
81
|
}
|
|
82
|
+
const isLayout = targetNodeTypeName === 'layoutSection';
|
|
83
|
+
const {
|
|
84
|
+
layoutSection,
|
|
85
|
+
layoutColumn
|
|
86
|
+
} = schema.nodes;
|
|
94
87
|
const result = [];
|
|
95
88
|
let currentContainerContent = [];
|
|
96
89
|
let hasCreatedContainer = false;
|
|
97
90
|
const flushCurrentContainer = () => {
|
|
98
|
-
if (currentContainerContent.length
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
91
|
+
if (currentContainerContent.length === 0) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (isLayout) {
|
|
95
|
+
// For layouts, create layoutSection with two layoutColumns
|
|
96
|
+
const columnOne = layoutColumn.createAndFill({}, removeDisallowedMarks(currentContainerContent, layoutColumn));
|
|
97
|
+
const columnTwo = layoutColumn.createAndFill();
|
|
98
|
+
if (!columnOne || !columnTwo) {
|
|
99
|
+
currentContainerContent = [];
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const layout = layoutSection.createAndFill({}, [columnOne, columnTwo]);
|
|
103
|
+
if (layout) {
|
|
104
|
+
result.push(layout);
|
|
102
105
|
hasCreatedContainer = true;
|
|
103
106
|
}
|
|
104
107
|
currentContainerContent = [];
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// For regular containers, create directly
|
|
112
|
+
const containerNode = targetNodeType.createAndFill({}, currentContainerContent);
|
|
113
|
+
if (containerNode) {
|
|
114
|
+
result.push(containerNode);
|
|
115
|
+
hasCreatedContainer = true;
|
|
105
116
|
}
|
|
117
|
+
currentContainerContent = [];
|
|
106
118
|
};
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
119
|
+
const processNode = node => {
|
|
120
|
+
const validationType = isLayout ? layoutColumn : targetNodeType;
|
|
121
|
+
const canWrapNode = validationType.validContent(Fragment.from(removeDisallowedMarks([node], validationType)));
|
|
122
|
+
|
|
123
|
+
// Node can be wrapped - add to current container content
|
|
124
|
+
if (canWrapNode) {
|
|
110
125
|
// remove marks from node as nested nodes don't usually support block marks
|
|
111
|
-
currentContainerContent.push(...removeDisallowedMarks([node],
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
// Text node (heading, paragraph) that can't be wrapped - convert to paragraph
|
|
119
|
-
// Example: heading can't go in blockquote, so convert to paragraph with same content
|
|
126
|
+
currentContainerContent.push(...removeDisallowedMarks([node], validationType));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Text node (heading, paragraph) that can't be wrapped - convert to paragraph
|
|
131
|
+
// Example: heading can't go in blockquote, so convert to paragraph with same content
|
|
132
|
+
if (isTextNode(node)) {
|
|
120
133
|
const paragraph = convertTextNodeToParagraph(node, schema);
|
|
121
134
|
if (paragraph) {
|
|
122
135
|
currentContainerContent.push(paragraph);
|
|
123
136
|
}
|
|
124
|
-
|
|
125
|
-
// All other nodes that cannot be wrapped (lists, containers, tables, media, macros) - break out
|
|
126
|
-
// This includes list nodes like taskList that can't be placed in certain containers
|
|
127
|
-
flushCurrentContainer();
|
|
128
|
-
result.push(node);
|
|
137
|
+
return;
|
|
129
138
|
}
|
|
130
|
-
|
|
139
|
+
|
|
140
|
+
// All other nodes that cannot be wrapped in the target node - break out
|
|
141
|
+
// Examples: same-type containers, tables in panels, layoutSections in layouts
|
|
142
|
+
flushCurrentContainer();
|
|
143
|
+
result.push(node);
|
|
144
|
+
};
|
|
145
|
+
nodes.forEach(processNode);
|
|
131
146
|
|
|
132
147
|
// Flush any remaining content into a container
|
|
133
148
|
flushCurrentContainer();
|
|
134
149
|
|
|
135
|
-
//
|
|
150
|
+
// Skip edge case handling for layouts since layouts always have columns
|
|
151
|
+
if (isLayout) {
|
|
152
|
+
return result.length > 0 ? result : nodes;
|
|
153
|
+
}
|
|
136
154
|
const finalResult = handleEmptyContainerEdgeCase(result, hasCreatedContainer, fromNode, targetNodeType, targetNodeTypeName, schema);
|
|
137
155
|
return finalResult.length > 0 ? finalResult : nodes;
|
|
138
156
|
};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { getTargetNodeTypeNameInContext } from '../transform-node-utils/utils';
|
|
2
2
|
import { flattenStep } from './flattenStep';
|
|
3
3
|
import { applyTargetTextTypeStep } from './steps/applyTargetTextTypeStep';
|
|
4
|
+
import { convertEachNodeStep } from './steps/convertEachNodeStep';
|
|
4
5
|
import { decisionListToListStep } from './steps/decisionListToListStep';
|
|
5
6
|
import { flattenListStep } from './steps/flattenListStep';
|
|
6
7
|
import { listToDecisionListStep } from './steps/listToDecisionListStep';
|
|
7
8
|
import { listToListStep } from './steps/listToListStep';
|
|
9
|
+
import { mergeNeighbourListsStep } from './steps/mergeNeighbourListsStep';
|
|
8
10
|
import { unwrapLayoutStep } from './steps/unwrapLayoutStep';
|
|
9
11
|
import { unwrapListStep } from './steps/unwrapListStep';
|
|
10
12
|
import { wrapBlockquoteToDecisionListStep } from './steps/wrapBlockquoteToDecisionListStep';
|
|
@@ -13,7 +15,6 @@ import { wrapTextToCodeblockStep } from './steps/wrapTextToCodeblock';
|
|
|
13
15
|
import { getNodeName, NODE_CATEGORY_BY_TYPE, toNodeTypeValue } from './types';
|
|
14
16
|
import { unwrapExpandStep } from './unwrapExpandStep';
|
|
15
17
|
import { unwrapStep } from './unwrapStep';
|
|
16
|
-
import { wrapIntoLayoutStep } from './wrapIntoLayoutStep';
|
|
17
18
|
import { wrapIntoListStep } from './wrapIntoListStep';
|
|
18
19
|
import { wrapStep } from './wrapStep';
|
|
19
20
|
|
|
@@ -50,10 +51,8 @@ const TRANSFORM_STEPS = {
|
|
|
50
51
|
multi: {
|
|
51
52
|
atomic: undefined,
|
|
52
53
|
container: [wrapMixedContentStep],
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
// TODO: EDITOR-4140 - Implement multi text transform
|
|
56
|
-
text: undefined,
|
|
54
|
+
list: [convertEachNodeStep, mergeNeighbourListsStep],
|
|
55
|
+
text: [convertEachNodeStep],
|
|
57
56
|
multi: undefined
|
|
58
57
|
}
|
|
59
58
|
};
|
|
@@ -65,15 +64,15 @@ const TRANSFORM_STEPS_OVERRIDE = {
|
|
|
65
64
|
paragraph: {
|
|
66
65
|
paragraph: null,
|
|
67
66
|
codeBlock: [wrapTextToCodeblockStep],
|
|
68
|
-
layoutSection: [
|
|
67
|
+
layoutSection: [wrapMixedContentStep]
|
|
69
68
|
},
|
|
70
69
|
heading: {
|
|
71
70
|
codeBlock: [wrapTextToCodeblockStep],
|
|
72
|
-
layoutSection: [
|
|
71
|
+
layoutSection: [wrapMixedContentStep]
|
|
73
72
|
},
|
|
74
73
|
panel: {
|
|
75
74
|
panel: null,
|
|
76
|
-
layoutSection: [unwrapStep,
|
|
75
|
+
layoutSection: [unwrapStep, wrapMixedContentStep],
|
|
77
76
|
codeBlock: [unwrapStep, flattenStep, wrapStep],
|
|
78
77
|
blockquote: [unwrapStep, wrapMixedContentStep],
|
|
79
78
|
taskList: null,
|
|
@@ -85,7 +84,7 @@ const TRANSFORM_STEPS_OVERRIDE = {
|
|
|
85
84
|
expand: null,
|
|
86
85
|
panel: [unwrapExpandStep, wrapMixedContentStep],
|
|
87
86
|
blockquote: [unwrapExpandStep, wrapMixedContentStep],
|
|
88
|
-
layoutSection: [unwrapExpandStep,
|
|
87
|
+
layoutSection: [unwrapExpandStep, wrapMixedContentStep],
|
|
89
88
|
paragraph: [unwrapExpandStep],
|
|
90
89
|
codeBlock: null,
|
|
91
90
|
heading: null
|
|
@@ -103,7 +102,7 @@ const TRANSFORM_STEPS_OVERRIDE = {
|
|
|
103
102
|
blockquote: null,
|
|
104
103
|
expand: [wrapStep],
|
|
105
104
|
nestedExpand: [wrapStep],
|
|
106
|
-
layoutSection: [
|
|
105
|
+
layoutSection: [wrapMixedContentStep],
|
|
107
106
|
codeBlock: null,
|
|
108
107
|
decisionList: [unwrapStep, wrapBlockquoteToDecisionListStep]
|
|
109
108
|
},
|
|
@@ -121,34 +120,34 @@ const TRANSFORM_STEPS_OVERRIDE = {
|
|
|
121
120
|
blockquote: [wrapStep],
|
|
122
121
|
expand: [wrapStep],
|
|
123
122
|
nestedExpand: [wrapStep],
|
|
124
|
-
layoutSection: [
|
|
123
|
+
layoutSection: [wrapMixedContentStep],
|
|
125
124
|
panel: [wrapStep],
|
|
126
125
|
heading: null
|
|
127
126
|
},
|
|
128
127
|
bulletList: {
|
|
129
128
|
bulletList: null,
|
|
130
129
|
codeBlock: null,
|
|
131
|
-
layoutSection: [
|
|
130
|
+
layoutSection: [wrapMixedContentStep],
|
|
132
131
|
decisionList: [flattenListStep, listToDecisionListStep],
|
|
133
132
|
heading: null
|
|
134
133
|
},
|
|
135
134
|
orderedList: {
|
|
136
135
|
orderedList: null,
|
|
137
136
|
codeBlock: null,
|
|
138
|
-
layoutSection: [
|
|
137
|
+
layoutSection: [wrapMixedContentStep],
|
|
139
138
|
decisionList: [flattenListStep, listToDecisionListStep],
|
|
140
139
|
heading: null
|
|
141
140
|
},
|
|
142
141
|
taskList: {
|
|
143
142
|
blockquote: null,
|
|
144
143
|
codeBlock: null,
|
|
145
|
-
layoutSection: [
|
|
144
|
+
layoutSection: [wrapMixedContentStep],
|
|
146
145
|
decisionList: [flattenListStep, listToDecisionListStep],
|
|
147
146
|
heading: null,
|
|
148
147
|
taskList: null
|
|
149
148
|
},
|
|
150
149
|
table: {
|
|
151
|
-
layoutSection: [
|
|
150
|
+
layoutSection: [wrapMixedContentStep],
|
|
152
151
|
blockquote: null,
|
|
153
152
|
panel: null,
|
|
154
153
|
codeBlock: null,
|
|
@@ -158,13 +157,13 @@ const TRANSFORM_STEPS_OVERRIDE = {
|
|
|
158
157
|
decisionList: null
|
|
159
158
|
},
|
|
160
159
|
mediaSingle: {
|
|
161
|
-
layoutSection: [
|
|
160
|
+
layoutSection: [wrapMixedContentStep],
|
|
162
161
|
codeBlock: null,
|
|
163
162
|
decisionList: null,
|
|
164
163
|
taskList: null
|
|
165
164
|
},
|
|
166
165
|
mediaGroup: {
|
|
167
|
-
layoutSection: [
|
|
166
|
+
layoutSection: [wrapMixedContentStep],
|
|
168
167
|
codeBlock: null,
|
|
169
168
|
decisionList: null,
|
|
170
169
|
bulletList: null,
|
|
@@ -176,10 +175,10 @@ const TRANSFORM_STEPS_OVERRIDE = {
|
|
|
176
175
|
bulletList: [decisionListToListStep],
|
|
177
176
|
orderedList: [decisionListToListStep],
|
|
178
177
|
taskList: [decisionListToListStep],
|
|
179
|
-
layoutSection: [
|
|
178
|
+
layoutSection: [wrapMixedContentStep]
|
|
180
179
|
},
|
|
181
180
|
blockCard: {
|
|
182
|
-
layoutSection: [
|
|
181
|
+
layoutSection: [wrapMixedContentStep],
|
|
183
182
|
blockquote: null,
|
|
184
183
|
codeBlock: null,
|
|
185
184
|
orderedList: null,
|
|
@@ -188,7 +187,7 @@ const TRANSFORM_STEPS_OVERRIDE = {
|
|
|
188
187
|
decisionList: null
|
|
189
188
|
},
|
|
190
189
|
embedCard: {
|
|
191
|
-
layoutSection: [
|
|
190
|
+
layoutSection: [wrapMixedContentStep],
|
|
192
191
|
blockquote: null,
|
|
193
192
|
panel: null,
|
|
194
193
|
codeBlock: null,
|
|
@@ -198,7 +197,7 @@ const TRANSFORM_STEPS_OVERRIDE = {
|
|
|
198
197
|
decisionList: null
|
|
199
198
|
},
|
|
200
199
|
extension: {
|
|
201
|
-
layoutSection: [
|
|
200
|
+
layoutSection: [wrapMixedContentStep],
|
|
202
201
|
codeBlock: null,
|
|
203
202
|
decisionList: null,
|
|
204
203
|
taskList: null,
|
|
@@ -206,7 +205,7 @@ const TRANSFORM_STEPS_OVERRIDE = {
|
|
|
206
205
|
bulletList: null
|
|
207
206
|
},
|
|
208
207
|
bodiedExtension: {
|
|
209
|
-
layoutSection: [
|
|
208
|
+
layoutSection: [wrapMixedContentStep],
|
|
210
209
|
blockquote: null,
|
|
211
210
|
expand: null,
|
|
212
211
|
panel: null,
|
|
@@ -217,8 +216,10 @@ const TRANSFORM_STEPS_OVERRIDE = {
|
|
|
217
216
|
decisionList: null
|
|
218
217
|
},
|
|
219
218
|
multi: {
|
|
220
|
-
// TODO: EDITOR-
|
|
221
|
-
|
|
219
|
+
// TODO: EDITOR-4140 - Implement multiple paragraphs/headings/codeblocks to heading transform
|
|
220
|
+
heading: null,
|
|
221
|
+
// TODO: EDITOR-4141 - Implement multiple codeblocks/headings to paragraph transform
|
|
222
|
+
paragraph: null
|
|
222
223
|
}
|
|
223
224
|
};
|
|
224
225
|
const getTransformStepsForNodeTypes = (selectedNodeTypeName, targetNodeTypeName) => {
|
|
@@ -232,7 +233,22 @@ const getTransformStepsForNodeTypes = (selectedNodeTypeName, targetNodeTypeName)
|
|
|
232
233
|
const steps = overrideSteps !== null && overrideSteps !== void 0 ? overrideSteps : TRANSFORM_STEPS[fromCategory][toCategory];
|
|
233
234
|
return steps;
|
|
234
235
|
};
|
|
235
|
-
|
|
236
|
+
/**
|
|
237
|
+
* Convert a list of nodes to a target node type.
|
|
238
|
+
* If no steps are found, the source nodes are returned unchanged.
|
|
239
|
+
* If steps are found, they are applied to the source nodes in order.
|
|
240
|
+
* If a step returns an empty array, the source nodes are returned.
|
|
241
|
+
* If a step returns a non-empty array, that array is returned.
|
|
242
|
+
* @param args - The conversion arguments
|
|
243
|
+
* @param args.sourceNodes - The list of nodes to convert
|
|
244
|
+
* @param args.targetNodeType - The type of node to convert into
|
|
245
|
+
* @param args.schema - The schema to use for the conversion
|
|
246
|
+
* @param args.isNested - Whether the conversion is nested
|
|
247
|
+
* @param args.targetAttrs - The attributes to use for the conversion
|
|
248
|
+
* @param args.parentNode - The parent node of the selected node
|
|
249
|
+
* @returns The converted list of nodes
|
|
250
|
+
*/
|
|
251
|
+
export const convertNodesToTargetType = ({
|
|
236
252
|
sourceNodes,
|
|
237
253
|
targetNodeType,
|
|
238
254
|
schema,
|
|
@@ -242,14 +258,13 @@ export const getOutputNodes = ({
|
|
|
242
258
|
}) => {
|
|
243
259
|
const sourceNode = sourceNodes.at(0);
|
|
244
260
|
if (!sourceNode) {
|
|
245
|
-
return;
|
|
261
|
+
return sourceNodes;
|
|
246
262
|
}
|
|
247
263
|
const selectedNodeTypeName = toNodeTypeValue(getNodeName(sourceNodes));
|
|
248
264
|
const initialTargetNodeTypeName = toNodeTypeValue(targetNodeType.name);
|
|
249
265
|
const targetNodeTypeName = getTargetNodeTypeNameInContext(initialTargetNodeTypeName, isNested, parentNode);
|
|
250
266
|
if (!selectedNodeTypeName || !targetNodeTypeName) {
|
|
251
|
-
|
|
252
|
-
return;
|
|
267
|
+
return sourceNodes;
|
|
253
268
|
}
|
|
254
269
|
const steps = getTransformStepsForNodeTypes(selectedNodeTypeName, targetNodeTypeName);
|
|
255
270
|
const context = {
|
|
@@ -260,7 +275,7 @@ export const getOutputNodes = ({
|
|
|
260
275
|
targetAttrs
|
|
261
276
|
};
|
|
262
277
|
if (!steps || steps.length === 0) {
|
|
263
|
-
return;
|
|
278
|
+
return sourceNodes;
|
|
264
279
|
}
|
|
265
280
|
return steps.reduce((nodes, step) => {
|
|
266
281
|
return step(nodes, context);
|