UNPKG

@atlaskit/editor-plugin-tasks-and-decisions

Version:

Tasks and decisions plugin for @atlaskit/editor-core

203 lines (202 loc) 7.03 kB
import { uuid } from '@atlaskit/adf-schema'; import { getBlockMarkAttrs, getFirstParagraphBlockMarkAttrs } from '@atlaskit/editor-common/lists'; import { createBlockTaskItem, isTaskList } from '@atlaskit/editor-common/transforms'; import { Slice, Fragment } from '@atlaskit/editor-prosemirror/model'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; /** * Transforms a paste slice to handle blockTaskItem nodes when pasting into task items. * * Initially we do not support user creation of blockTaskItem - it is intended primarily * for TinyMCE migration purposes however that may change in the future. * This function handles the behavior to work around Prosemirror behaviour which decides * that blockTaskItem is the appropriate node to use here. * * @param slice - The slice being pasted * @param view - The editor view where the paste is occurring * @returns The transformed slice with blockTaskItems converted to taskItems when appropriate * * @example * ```typescript * const transformedSlice = tempTransformSliceToRemoveBlockTaskItem(pasteSlice, editorView); * ``` * * @see {@link https://hello.atlassian.net/wiki/spaces/EDITOR/pages/5626622054/Block+elements+in+task+-+Decision+log#Can-users-create-block-task-items%3F} for reasoning */ const getTaskPasteContext = view => { var _schema$marks; const { schema, selection } = view.state; const { taskItem, blockTaskItem, paragraph } = schema.nodes; const fontSize = (_schema$marks = schema.marks) === null || _schema$marks === void 0 ? void 0 : _schema$marks.fontSize; const { $from } = selection; const currentParent = $from.parent; const currentNode = typeof $from.node === 'function' ? $from.node() : undefined; if ((currentParent === null || currentParent === void 0 ? void 0 : currentParent.type) === taskItem || (currentNode === null || currentNode === void 0 ? void 0 : currentNode.type) === taskItem) { return { isInTaskContext: true, smallTextAttrs: false }; } if (!blockTaskItem) { return { isInTaskContext: false, smallTextAttrs: false }; } if ((currentParent === null || currentParent === void 0 ? void 0 : currentParent.type) === blockTaskItem || (currentNode === null || currentNode === void 0 ? void 0 : currentNode.type) === blockTaskItem) { return { isInTaskContext: true, smallTextAttrs: fontSize ? getFirstParagraphBlockMarkAttrs(currentParent !== null && currentParent !== void 0 ? currentParent : currentNode, fontSize) : false }; } if ((currentParent === null || currentParent === void 0 ? void 0 : currentParent.type) === paragraph && $from.depth > 0 && $from.node($from.depth - 1).type === blockTaskItem) { return { isInTaskContext: true, smallTextAttrs: fontSize ? getBlockMarkAttrs(currentParent, fontSize) : false }; } return { isInTaskContext: false, smallTextAttrs: false }; }; const convertTaskItemToBlockTaskItem = (node, schema, smallTextAttrs) => { const { fontSize } = schema.marks; return createBlockTaskItem({ attrs: node.attrs, content: node.content, marks: [fontSize.create(smallTextAttrs)], schema }); }; const addSmallTextToBlockTaskItem = (node, schema, smallTextAttrs) => { const { paragraph } = schema.nodes; const { fontSize } = schema.marks; const newContent = []; node.content.forEach(child => { if (child.type === paragraph) { newContent.push(paragraph.createChecked(child.attrs, child.content, child.marks.filter(mark => mark.type !== fontSize).concat(fontSize.create(smallTextAttrs)))); } else { newContent.push(child); } }); return node.type.create(node.attrs, newContent); }; const normalizeBlockTaskItemToTaskItems = (node, schema) => { const { taskItem, blockTaskItem, paragraph } = schema.nodes; if (!blockTaskItem || node.type !== blockTaskItem) { return [node]; } let allChildrenAreParagraphs = true; node.content.forEach(child => { if (child.type !== paragraph) { allChildrenAreParagraphs = false; } }); if (allChildrenAreParagraphs && node.childCount > 0) { const transformedContent = []; node.content.forEach(paragraphNode => { transformedContent.push(taskItem.create({ localId: uuid.generate(), state: node.attrs.state || 'TODO' }, paragraphNode.content)); }); return transformedContent; } return [node]; }; const transformSliceContent = (slice, transformNode) => { const transformedContent = []; slice.content.forEach(node => { transformedContent.push(...transformNode(node)); }); if (transformedContent.length !== slice.content.childCount || transformedContent.some((node, idx) => node !== slice.content.child(idx))) { const newFragment = Fragment.from(transformedContent); return new Slice(newFragment, slice.openStart, slice.openEnd); } return slice; }; const transformSliceToRemoveBlockTaskItemLegacy = (slice, view) => { const { schema } = view.state; const { taskItem, blockTaskItem } = schema.nodes; const isInTaskItem = view.state.selection.$from.node().type === taskItem; if (!isInTaskItem || !blockTaskItem) { return slice; } return transformSliceContent(slice, node => normalizeBlockTaskItemToTaskItems(node, schema)); }; export const normalizeNodeForTaskTextSize = (node, schema, smallTextAttrs) => { const { taskList, taskItem, blockTaskItem } = schema.nodes; const { fontSize } = schema.marks; if (!smallTextAttrs) { return [node]; } if (isTaskList(node.type)) { const transformedChildren = []; node.content.forEach(child => { transformedChildren.push(...normalizeNodeForTaskTextSize(child, schema, smallTextAttrs)); }); return [taskList.create(node.attrs, transformedChildren)]; } if (blockTaskItem && fontSize) { if (node.type === taskItem) { return [convertTaskItemToBlockTaskItem(node, schema, smallTextAttrs)]; } if (node.type === blockTaskItem) { return [addSmallTextToBlockTaskItem(node, schema, smallTextAttrs)]; } } return [node]; }; const normalizeNodeForTaskPaste = (node, schema, smallTextAttrs) => { if (smallTextAttrs) { return normalizeNodeForTaskTextSize(node, schema, smallTextAttrs); } return normalizeBlockTaskItemToTaskItems(node, schema); }; export const tempTransformSliceToRemoveBlockTaskItem = (slice, view) => { const fontSizeExperimentEnabled = expValEquals('platform_editor_small_font_size', 'isEnabled', true); if (!fontSizeExperimentEnabled) { return transformSliceToRemoveBlockTaskItemLegacy(slice, view); } const { blockTaskItem } = view.state.schema.nodes; const { isInTaskContext, smallTextAttrs } = getTaskPasteContext(view); if (!isInTaskContext || !blockTaskItem) { return slice; } return transformSliceContent(slice, node => normalizeNodeForTaskPaste(node, view.state.schema, smallTextAttrs)); };