@atlaskit/editor-plugin-list
Version:
List plugin for @atlaskit/editor-core
83 lines (78 loc) • 4.14 kB
JavaScript
import { insertContentDeleteRange, isListNode } from '@atlaskit/editor-common/utils';
import { Fragment } from '@atlaskit/editor-prosemirror/model';
// Case for when a users selection is at the end of a paragraph, the paragraph
// is followed by a list, and they delete forward
export var joinListItemWithParagraph = function joinListItemWithParagraph(_ref) {
var tr = _ref.tr,
$next = _ref.$next,
$head = _ref.$head;
// For empty paragraphs before a list
if ($head.parent.content.size < 1) {
insertContentDeleteRange(tr, function (tr) {
return tr.doc.resolve($head.pos);
}, [], [[$head.pos - 1, $head.pos]]);
return true;
}
var paragraphPosition = $head.pos;
var list = tr.doc.nodeAt($next.pos - 1);
var firstListItem = tr.doc.nodeAt($next.pos);
if (!list || !firstListItem) {
return false;
}
var firstChildNodeOfFirstListItem = firstListItem.firstChild;
if (!firstChildNodeOfFirstListItem) {
return false;
}
var lastChildOfFirstListItem = firstListItem.lastChild;
var firstGrandchildOfFirstListItem = firstChildNodeOfFirstListItem.firstChild;
var firstListItemHasOneChildWithNoNestedLists = hasSingleChild(firstListItem) && firstChildNodeOfFirstListItem.childCount < 2 && $next.nodeAfter;
var firstListItemContainsParagraphAndNestedList = !hasSingleChild(firstListItem) && lastChildOfFirstListItem && isListNode(lastChildOfFirstListItem);
var insertions = [];
var deletions = [];
// For lists that only have one list item with no children - need to remove remaining list
if (hasSingleChild(list) && hasSingleChild(firstListItem) && $next.nodeAfter) {
deletions.push([tr.mapping.map($next.pos - 1), tr.mapping.map($next.pos + $next.nodeAfter.nodeSize + 1)]);
}
// For first list items that have a paragraph and a list
if (firstListItemContainsParagraphAndNestedList) {
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
var firstListItemNestedList = Fragment.from(lastChildOfFirstListItem.content);
insertions.push([firstListItemNestedList, tr.mapping.map($next.pos)]);
}
// For first list item has one child & no nested lists OR first list items that have a paragraph and a list
if (firstListItemHasOneChildWithNoNestedLists || firstListItemContainsParagraphAndNestedList) {
deletions.push([tr.mapping.map($next.pos), tr.mapping.map($next.pos + firstListItem.nodeSize - 1)]);
var firstListItemText = Fragment.from(firstChildNodeOfFirstListItem.content);
insertions.push([firstListItemText, paragraphPosition]);
insertContentDeleteRange(tr, function (tr) {
return tr.doc.resolve($head.pos);
}, insertions, deletions);
return true;
}
// For any first list items that have multiple children (eg. multiple paragraphs)
if (firstListItem.childCount > 1) {
insertions.push([Fragment.from(firstChildNodeOfFirstListItem.content), paragraphPosition]);
deletions.push([tr.mapping.map($next.pos + 1), tr.mapping.map($next.pos + firstChildNodeOfFirstListItem.nodeSize + 1)]);
insertContentDeleteRange(tr, function (tr) {
return tr.doc.resolve($head.pos);
}, insertions, deletions);
return true;
}
// For any remaining first list items that have a single child (eg. single paragraph, multiple lines of text)
if (firstGrandchildOfFirstListItem && firstGrandchildOfFirstListItem.type.name === 'hardBreak') {
var nodeSizeOfGrandchild = firstGrandchildOfFirstListItem ? firstGrandchildOfFirstListItem.nodeSize : 0;
deletions.push([tr.mapping.map($next.pos + 2), tr.mapping.map($next.pos + 2 + nodeSizeOfGrandchild)]);
} else {
insertions.push([Fragment.from(firstChildNodeOfFirstListItem.content), paragraphPosition]);
var nodeSizeOfFirstChild = firstChildNodeOfFirstListItem.nodeSize;
deletions.push([tr.mapping.map($next.pos), tr.mapping.map($next.pos + 2 + nodeSizeOfFirstChild)]);
}
insertContentDeleteRange(tr, function (tr) {
return tr.doc.resolve($head.pos);
}, insertions, deletions);
return true;
};
var hasSingleChild = function hasSingleChild(node) {
return node.childCount === 1;
};