@atlaskit/editor-plugin-paste-options-toolbar
Version:
Paste options toolbar for @atlaskit/editor-core
176 lines (170 loc) • 7.61 kB
JavaScript
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;
}
}