UNPKG

@atlaskit/editor-plugin-paste-options-toolbar

Version:

Paste options toolbar for @atlaskit/editor-core

176 lines (170 loc) 7.61 kB
import { logException } from '@atlaskit/editor-common/monitoring'; import { md } from '@atlaskit/editor-common/paste'; import { MarkdownTransformer } from '@atlaskit/editor-markdown-transformer'; import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model'; import { Selection } from '@atlaskit/editor-prosemirror/state'; import { ReplaceStep } from '@atlaskit/editor-prosemirror/transform'; import { escapeLinks } from './index'; export var formatMarkdown = function formatMarkdown(tr, pluginState) { var pasteStartPos = pluginState.pasteStartPos; var pasteEndPos = pluginState.pasteEndPos; var plaintext = pluginState.plaintext; if (pasteStartPos < 0) { return tr; } var resolvedPasteStartPos = tr.doc.resolve(pasteStartPos); var parentOffset = resolvedPasteStartPos.parentOffset; if (parentOffset === 0 && resolvedPasteStartPos.depth > 0) { pasteStartPos = resolvedPasteStartPos.before(); } var markdownSlice = getMarkdownSlice(plaintext, tr.doc.type.schema, tr.selection); if (!markdownSlice) { return tr; } pasteSliceIntoTransactionWithSelectionAdjust({ tr: tr, pasteStartPos: pasteStartPos, pasteEndPos: pasteEndPos, slice: markdownSlice }); return tr; }; export var formatRichText = function formatRichText(tr, pluginState) { var pasteStartPos = pluginState.pasteStartPos; var pasteEndPos = pluginState.pasteEndPos; var richTextSlice = pluginState.richTextSlice; if (pasteStartPos < 0) { return tr; } if (richTextSlice.content.size === 0) { return tr; } var resolvedPasteStartPos = tr.doc.resolve(pasteStartPos); var parentOffset = resolvedPasteStartPos.parentOffset; if (parentOffset === 0 && resolvedPasteStartPos.depth > 0) { pasteStartPos = resolvedPasteStartPos.before(); } richTextSliceTransactionWithSelectionAdjust({ tr: tr, pasteStartPos: pasteStartPos, pasteEndPos: pasteEndPos, slice: richTextSlice }); return tr; }; export var formatPlainText = function formatPlainText(tr, pluginState) { var pasteStartPos = pluginState.pasteStartPos; var pasteEndPos = pluginState.pasteEndPos; var plaintext = pluginState.plaintext; //not possible to create plain text slice with empty string if (pasteStartPos < 0 || plaintext === '') { return tr; } var resolvedPasteStartPos = tr.doc.resolve(pasteStartPos); var parentOffset = resolvedPasteStartPos.parentOffset; if (parentOffset === 0 && resolvedPasteStartPos.depth > 0) { pasteStartPos = resolvedPasteStartPos.before(); } var schema = tr.doc.type.schema; var plainTextNode = schema.text(plaintext); var plainTextFragment = Fragment.from(schema.nodes.paragraph.createAndFill(null, plainTextNode)); var plainTextSlice = new Slice(plainTextFragment, resolvedPasteStartPos.depth, resolvedPasteStartPos.depth); pasteSliceIntoTransactionWithSelectionAdjust({ tr: tr, pasteStartPos: pasteStartPos, pasteEndPos: pasteEndPos, slice: plainTextSlice }); return tr; }; function pasteSliceIntoTransactionWithSelectionAdjust(_ref) { var tr = _ref.tr, pasteStartPos = _ref.pasteStartPos, pasteEndPos = _ref.pasteEndPos, slice = _ref.slice; tr.replaceRange(pasteStartPos, pasteEndPos, slice); // ProseMirror doesn't give a proper way to tell us where something was inserted. // However, we can know "how" it inserted something. // // So, instead of weird depth calculations, we can use the step produced by the transform. // For instance: // The `replaceStep.to and replaceStep.from`, tell us the real position // where the content will be insert. // Then, we can use the `tr.mapping.map` to the updated position after the replace operation var replaceStep = tr.steps[0]; if (!(replaceStep instanceof ReplaceStep)) { return tr; } var lastInsertNode = replaceStep.slice.content.lastChild; var emptyNodeReference = lastInsertNode === null || lastInsertNode === void 0 ? void 0 : lastInsertNode.type.createAndFill(); var isLastNodeEmpty = (emptyNodeReference === null || emptyNodeReference === void 0 ? void 0 : emptyNodeReference.nodeSize) === (lastInsertNode === null || lastInsertNode === void 0 ? void 0 : lastInsertNode.nodeSize); var isStepSplitingTarget = !(lastInsertNode !== null && lastInsertNode !== void 0 && lastInsertNode.isLeaf) && isLastNodeEmpty; var $nextHead = tr.doc.resolve(tr.mapping.map(replaceStep.to)); var $nextPosition = isStepSplitingTarget && $nextHead.depth > 0 ? tr.doc.resolve($nextHead.before()) : $nextHead; // The findFrom will make search for both: TextSelection and NodeSelections. var nextSelection = Selection.findFrom($nextPosition, -1); if (nextSelection) { tr.setSelection(nextSelection); } } function richTextSliceTransactionWithSelectionAdjust(_ref2) { var tr = _ref2.tr, pasteStartPos = _ref2.pasteStartPos, pasteEndPos = _ref2.pasteEndPos, slice = _ref2.slice; tr.replaceRange(pasteStartPos, pasteEndPos, slice); // ProseMirror doesn't give a proper way to tell us where something was inserted. // However, we can know "how" it inserted something. // // So, instead of weird depth calculations, we can use the step produced by the transform. // For instance: // The `replaceStep.to and replaceStep.from`, tell us the real position // where the content will be insert. // Then, we can use the `tr.mapping.map` to the updated position after the replace operation var replaceStep = tr.steps[0]; if (!(replaceStep instanceof ReplaceStep)) { return tr; } var nextPosition = tr.mapping.map(replaceStep.to); // The findFrom will make search for both: TextSelection and NodeSelections. var nextSelection = Selection.findFrom(tr.doc.resolve(Math.min(nextPosition, tr.doc.content.size)), -1); if (nextSelection) { tr.setSelection(nextSelection); } } export function getMarkdownSlice(text, schema, selection) { var targetOpenStartNode = selection.$from.parent; var targetOpenEndNode = selection.$to.parent; try { var _doc$content$firstChi, _doc$content$lastChil; var textInput = text; // Ignored via go/ees005 // eslint-disable-next-line require-unicode-regexp var textSplitByCodeBlock = textInput.split(/```/); for (var i = 0; i < textSplitByCodeBlock.length; i++) { if (i % 2 === 0) { // Ignored via go/ees005 // eslint-disable-next-line require-unicode-regexp, @atlassian/perf-linting/no-expensive-split-replace -- Ignored via go/ees017 (to be fixed) textSplitByCodeBlock[i] = textSplitByCodeBlock[i].replace(/\\/g, '\\\\'); } } textInput = textSplitByCodeBlock.join('```'); var atlassianMarkDownParser = new MarkdownTransformer(schema, md); var doc = atlassianMarkDownParser.parse(escapeLinks(textInput)); if (!doc || !doc.content) { return; } var canMergeOpenStart = targetOpenStartNode.type === ((_doc$content$firstChi = doc.content.firstChild) === null || _doc$content$firstChi === void 0 ? void 0 : _doc$content$firstChi.type); var canMergeOpenEnd = targetOpenEndNode.type === ((_doc$content$lastChil = doc.content.lastChild) === null || _doc$content$lastChil === void 0 ? void 0 : _doc$content$lastChil.type); var $start = Selection.atStart(doc).$from; var $end = Selection.atEnd(doc).$from; var openStart = canMergeOpenStart ? $start.depth : 0; var openEnd = canMergeOpenEnd ? $end.depth : 0; return new Slice(doc.content, openStart, openEnd); } catch (error) { logException(error, { location: 'editor-plugin-paste-options-toolbar/util' }); return; } }