@atlaskit/editor-plugin-block-menu
Version:
BlockMenu plugin for @atlaskit/editor-core
126 lines • 6.26 kB
JavaScript
import { expandSelectionToBlockRange } from '@atlaskit/editor-common/selection/utils';
import { Fragment } from '@atlaskit/editor-prosemirror/model';
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
import { isTransformDisabledBasedOnStepsConfig } from '../editor-commands/transform-node-utils/transform';
import { toNodeTypeValue } from '../editor-commands/transform-node-utils/types';
import { getBlockNodesInRange, getTargetNodeTypeNameInContext } from '../editor-commands/transform-node-utils/utils';
import { getSingleTransformSourceNode, isExtensionTransformSource } from './transformSource';
const getTargetMarks = (schema, currentMarks, marksToAdd, marksToRemove) => {
if (!marksToAdd && !marksToRemove) {
return undefined;
}
let targetMarks = currentMarks;
for (const name of marksToRemove !== null && marksToRemove !== void 0 ? marksToRemove : []) {
const markType = schema.marks[name];
if (markType) {
targetMarks = markType.removeFromSet(targetMarks);
}
}
for (const [name, attrs] of Object.entries(marksToAdd !== null && marksToAdd !== void 0 ? marksToAdd : {})) {
const markType = schema.marks[name];
if (markType) {
targetMarks = markType.create(attrs).addToSet(markType.removeFromSet(targetMarks));
}
}
return targetMarks;
};
export const canParentContainNodeType = (schema, selectedNodeTypeName, parentNode, nodeTypeName, nodeTypeAttrs, selectedNode, marksToAdd, marksToRemove) => {
const adjustedNodeTypeName = getTargetNodeTypeNameInContext(nodeTypeName, true, parentNode, schema);
if (!adjustedNodeTypeName) {
return false;
}
const nodeType = schema.nodes[adjustedNodeTypeName];
let content = null;
const nodesThatCantBeNestedInNestedExpand = ['blockCard', 'embedCard', 'table'];
if (nodesThatCantBeNestedInNestedExpand.includes(selectedNodeTypeName) && (adjustedNodeTypeName === 'expand' || adjustedNodeTypeName === 'nestedExpand')) {
const node = schema.nodes[selectedNodeTypeName];
content = node.createAndFill();
}
if (isExperimentEnabled('platform_editor_block_menu_small_text') && selectedNode && (marksToAdd || marksToRemove)) {
const targetMarks = getTargetMarks(schema, selectedNode.marks, marksToAdd, marksToRemove);
const targetNode = nodeType.createAndFill(nodeTypeAttrs, content, targetMarks);
return Boolean(targetNode && parentNode.type.validContent(Fragment.from(targetNode)));
}
return parentNode.type.validContent(Fragment.from(nodeType.createAndFill(nodeTypeAttrs, content)));
};
const isHeadingToHeadingTransformEnabled = (selectedNode, targetNodeTypeAttrs) => {
var _selectedNode$attrs;
const selectedLevel = (_selectedNode$attrs = selectedNode.attrs) === null || _selectedNode$attrs === void 0 ? void 0 : _selectedNode$attrs.level;
const targetLevel = targetNodeTypeAttrs === null || targetNodeTypeAttrs === void 0 ? void 0 : targetNodeTypeAttrs.level;
if (selectedLevel === undefined || targetLevel === undefined) {
return false;
}
return selectedLevel !== targetLevel;
};
const isTransformEnabledForNode = (node, targetNodeTypeName, targetNodeTypeAttrs, marksToAdd, marksToRemove, isNested, parent, schema) => {
const selectedNodeTypeName = toNodeTypeValue(node.type.name);
if (!selectedNodeTypeName) {
return false;
}
if (selectedNodeTypeName === targetNodeTypeName) {
const removesTargetMarks = marksToRemove === null || marksToRemove === void 0 ? void 0 : marksToRemove.some(name => {
const markType = schema.marks[name];
return markType ? Boolean(markType.isInSet(node.marks)) : false;
});
const addsOrReplacesTargetMarks = Object.entries(marksToAdd !== null && marksToAdd !== void 0 ? marksToAdd : {}).some(([name, attrs]) => {
var _markType$isInSet;
const markType = schema.marks[name];
return markType ? !((_markType$isInSet = markType.isInSet(node.marks)) !== null && _markType$isInSet !== void 0 && _markType$isInSet.eq(markType.create(attrs))) : false;
});
if (removesTargetMarks || addsOrReplacesTargetMarks) {
if (isNested && !canParentContainNodeType(schema, selectedNodeTypeName, parent, targetNodeTypeName, targetNodeTypeAttrs, node, marksToAdd, marksToRemove)) {
return false;
}
return true;
}
}
const isDisabledByStepsConfig = isTransformDisabledBasedOnStepsConfig(selectedNodeTypeName, targetNodeTypeName);
if (isDisabledByStepsConfig) {
return false;
}
if (selectedNodeTypeName === 'heading' && targetNodeTypeName === 'heading') {
return isHeadingToHeadingTransformEnabled(node, targetNodeTypeAttrs);
}
if (isNested && !canParentContainNodeType(schema, selectedNodeTypeName, parent, targetNodeTypeName, targetNodeTypeAttrs, node, marksToAdd, marksToRemove)) {
return false;
}
return true;
};
export const isTransformToTargetDisabled = ({
selection,
targetNodeTypeName,
targetNodeTypeAttrs,
targetNodeMarkChanges,
transformRegistry
}) => {
const {
range
} = expandSelectionToBlockRange(selection);
if (!range) {
return false;
}
const sourceNode = getSingleTransformSourceNode(selection, range);
if (isExtensionTransformSource(sourceNode)) {
var _transformRegistry$re;
return (transformRegistry === null || transformRegistry === void 0 ? void 0 : (_transformRegistry$re = transformRegistry.resolve({
source: sourceNode.toJSON(),
targetTypeName: targetNodeTypeName
})) === null || _transformRegistry$re === void 0 ? void 0 : _transformRegistry$re.status) !== 'supported';
}
const selectedNodes = getBlockNodesInRange(range);
const parent = range.parent;
const isNested = range.depth >= 1;
const {
schema
} = selection.$from.doc.type;
const {
marksToAdd,
marksToRemove
} = targetNodeMarkChanges !== null && targetNodeMarkChanges !== void 0 ? targetNodeMarkChanges : {};
const supportedTargetNodeTypeName = toNodeTypeValue(targetNodeTypeName);
if (!supportedTargetNodeTypeName) {
return true;
}
const isEnabledForAnyNode = selectedNodes.some(node => isTransformEnabledForNode(node, supportedTargetNodeTypeName, targetNodeTypeAttrs, marksToAdd, marksToRemove, isNested, parent, schema));
return !isEnabledForAnyNode;
};