UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

163 lines (159 loc) 7.04 kB
import { Fragment } from '@atlaskit/editor-prosemirror/model'; import { isListItemNode, isListNode } from '../utils'; import { wrapTaskListIntoListAbove } from './replace-content'; export function isListNodeValidContent(node) { var bulletList = node.type.schema.nodes.bulletList; if (!bulletList) { return false; } var listFragment = Fragment.from(bulletList.createAndFill()); return !isListItemNode(node) && node.type.validContent(listFragment); } export var JoinDirection = /*#__PURE__*/function (JoinDirection) { JoinDirection[JoinDirection["LEFT"] = 1] = "LEFT"; JoinDirection[JoinDirection["RIGHT"] = -1] = "RIGHT"; return JoinDirection; }({}); export var joinSiblingLists = function joinSiblingLists(_ref) { var tr = _ref.tr, direction = _ref.direction, forceListType = _ref.forceListType; var result = { orderedList: 0, bulletList: 0 }; var doc = tr.doc, _tr$selection = tr.selection, $from = _tr$selection.$from, $to = _tr$selection.$to, selection = tr.selection; var range = $from.blockRange($to, isListNodeValidContent); if (!range) { return result; } var rootListNode = doc.nodeAt(range.start); var from = isListNode(rootListNode) ? range.start : 0; var to = isListNode(rootListNode) ? range.end : tr.doc.content.size; var joins = []; doc.nodesBetween(from, to, function (node, pos, parent) { var resolvedPos = doc.resolve(pos); var nodeBefore = resolvedPos.nodeBefore, nodeAfter = resolvedPos.nodeAfter; if (!nodeBefore || !nodeAfter || !isListNode(nodeBefore) || !isListNode(nodeAfter)) { return; } var isNestedList = isListItemNode(parent); if (!isNestedList && nodeBefore.type !== nodeAfter.type && !forceListType) { return; } var index = resolvedPos.index(); var positionPreviousNode = resolvedPos.posAtIndex(index - 1); var positionCurrentNode = resolvedPos.posAtIndex(index); // If the previous node is part of the selection, OR // If the previous node is not part of the selection and the previous node has the same list type that we’re converting to var joinBefore = positionPreviousNode >= from || nodeBefore.type === forceListType; if (forceListType) { if (joinBefore) { tr.setNodeMarkup(positionPreviousNode, forceListType); } tr.setNodeMarkup(positionCurrentNode, forceListType); } if (isNestedList && nodeBefore.type !== nodeAfter.type) { var nodeType = direction === JoinDirection.RIGHT ? nodeAfter.type : nodeBefore.type; tr.setNodeMarkup(positionPreviousNode, nodeType); } if (joinBefore) { joins.push(pos); } }); if (selection.empty && rootListNode && isListNode(rootListNode)) { var resolvedPos = doc.resolve(range.start + rootListNode.nodeSize); var nodeBefore = resolvedPos.nodeBefore, nodeAfter = resolvedPos.nodeAfter; if (nodeBefore && nodeAfter && isListNode(nodeBefore) && isListNode(nodeAfter) && nodeAfter.type === nodeBefore.type) { joins.push(resolvedPos.pos); } } for (var i = joins.length - 1; i >= 0; i--) { var listNode = tr.doc.nodeAt(joins[i]); var listName = listNode === null || listNode === void 0 ? void 0 : listNode.type.name; if (listName && (listName === 'orderedList' || listName === 'bulletList')) { var amount = result[listName] || 0; result[listName] = amount + 1; } tr.join(joins[i]); } return result; }; /** * Returns the prosemirror position for the child at give index inside the parent node. * Example: Considering doc structure as below using the function * passing parent resolved position for li and index 2 * would return starting position of taskList * DOC STRUCTURE: * ol() * ( li( * p('text'), * ul(content), * taskList(), * ) * ) * @param $from Starting resolved position for the parent node of the child we are looking for. * @param index Index of the child node we want the position for. * @returns */ var findStartPositionOfChildWithIndex = function findStartPositionOfChildWithIndex($from, index) { var parent = $from.node(); var currentPos = $from.pos + 1; for (var i = 0; i < index; i++) { currentPos += parent.child(i).nodeSize; } return currentPos; }; var findGrandParentResolvedPos = function findGrandParentResolvedPos(tr, $from) { return $from.depth > 2 ? tr.doc.resolve($from.start($from.depth - 2)) : null; }; var findNestedTaskListsIndexAtSameLevel = function findNestedTaskListsIndexAtSameLevel(tr, $from) { /* Currently our cursor would be inside a pargraph of a list of type numbered/bullet list, we need to find the grandparent of the cursor which is the list at same level of taskList. We can get the root list item(inside which various lists are being resolved before outdenting) by going one depth above that list. */ var nestedListResolvedPos = findGrandParentResolvedPos(tr, $from); var rootListItem = nestedListResolvedPos === null || nestedListResolvedPos === void 0 ? void 0 : nestedListResolvedPos.node(nestedListResolvedPos.depth - 1); var nestedTaskListsIndexes = []; var rootListItemChildCount = (rootListItem === null || rootListItem === void 0 ? void 0 : rootListItem.childCount) || 0; // first child of list item is always paragraph (i = 0) so we start from 1 for (var i = 1; i < rootListItemChildCount; i++) { if ((rootListItem === null || rootListItem === void 0 ? void 0 : rootListItem.child(i).type.name) === 'taskList') { nestedTaskListsIndexes.push(i); } } return nestedTaskListsIndexes; }; export var processNestedTaskListsInSameLevel = function processNestedTaskListsInSameLevel(tr) { var $from = tr.selection.$from; var nestedTaskListIndexes = findNestedTaskListsIndexAtSameLevel(tr, $from); if (nestedTaskListIndexes.length === 0) { return; } var nestedListResolvedPos = findGrandParentResolvedPos(tr, $from); var rootListItemStart = nestedListResolvedPos === null || nestedListResolvedPos === void 0 ? void 0 : nestedListResolvedPos.start(nestedListResolvedPos.depth - 1); /* We need not wrap the taskList present above other lists since it doesn't affect the flow. */ var nestedTaskListIndexesToWrap = nestedTaskListIndexes.filter(function (index) { return index > 1; }); /* Wraps the taskLists present at each index mentioned in the nestedTaskListIndexesToWrap to the list above it. After each wrap the indexes changes since two lists are being merged into one, so we keep track of it and use it to access actual calculated taskList indexes. */ if (rootListItemStart) { var taskListsFixedNested = 0; nestedTaskListIndexesToWrap.forEach(function (index) { wrapTaskListIntoListAbove(tr, findStartPositionOfChildWithIndex(tr.doc.resolve(rootListItemStart), index - taskListsFixedNested), findStartPositionOfChildWithIndex(tr.doc.resolve(rootListItemStart), index - 1 - taskListsFixedNested)); taskListsFixedNested++; }); } return; };