UNPKG

@atlaskit/editor-plugin-list

Version:

List plugin for @atlaskit/editor-core

97 lines (93 loc) 3.78 kB
import { GapCursorSelection } from '@atlaskit/editor-common/selection'; import { autoJoinTr } from '@atlaskit/editor-common/utils'; import { Fragment, NodeRange, Slice } from '@atlaskit/editor-prosemirror/model'; import { canSplit, findWrapping, ReplaceAroundStep } from '@atlaskit/editor-prosemirror/transform'; import { isWrappingPossible } from '../utils/selection'; /** * Wraps the selection in a list with the given type. If this results in * two adjacent lists of the same type, those will be joined together. */ export function wrapInListAndJoin(nodeType, tr) { wrapInList(nodeType)(tr); autoJoinTr(tr, function (before, after) { return before.type === after.type && before.type === nodeType; }); } // eslint-disable-next-line @typescript-eslint/no-explicit-any /** * Wraps the selection in a list with the given type and attributes. * * Adapted from https://github.com/ProseMirror/prosemirror-schema-list/blob/master/src/schema-list.js#L64-L89 */ export function wrapInList(listType, attrs) { return function (tr) { var _tr$selection = tr.selection, $from = _tr$selection.$from, $to = _tr$selection.$to; var range; if (tr.selection instanceof GapCursorSelection && $from.nodeAfter && isWrappingPossible(listType, tr.selection)) { var nodeSize = $from.nodeAfter.nodeSize || 1; range = $from.blockRange($from.doc.resolve($from.pos + nodeSize)); } else { range = $from.blockRange($to); } var doJoin = false; var outerRange = range; if (!range) { return false; } // This is at the top of an existing list item if (range.depth >= 2 && // @ts-ignore - missing type for compatibleContent $from.node(range.depth - 1).type.compatibleContent(listType) && range.startIndex === 0) { // Don't do anything if this is the top of the list if ($from.index(range.depth - 1) === 0) { return false; } var $insert = tr.doc.resolve(range.start - 2); outerRange = new NodeRange($insert, $insert, range.depth); if (range.endIndex < range.parent.childCount) { range = new NodeRange($from, tr.doc.resolve($to.end(range.depth)), range.depth); } doJoin = true; } // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion var wrap = findWrapping(outerRange, listType, attrs, range); if (!wrap) { return false; } tr = doWrapInList(tr, range, wrap, doJoin, listType); return true; }; } /** * Internal function used by wrapInList * * Adapted from https://github.com/ProseMirror/prosemirror-schema-list/blob/master/src/schema-list.js#L91-L112 */ function doWrapInList(tr, range, wrappers, joinBefore, listType) { var content = Fragment.empty; for (var i = wrappers.length - 1; i >= 0; i--) { content = Fragment.from(wrappers[i].type.create(wrappers[i].attrs, content)); } tr.step(new ReplaceAroundStep(range.start - (joinBefore ? 2 : 0), range.end, range.start, range.end, new Slice(content, 0, 0), wrappers.length, true)); var found = 0; for (var _i = 0; _i < wrappers.length; _i++) { if (wrappers[_i].type === listType) { found = _i + 1; } } var splitDepth = wrappers.length - found; var splitPos = range.start + wrappers.length - (joinBefore ? 2 : 0); var parent = range.parent; for (var _i2 = range.startIndex, e = range.endIndex, first = true; _i2 < e; _i2++, first = false) { if (!first && canSplit(tr.doc, splitPos, splitDepth)) { // eslint-disable-next-line @atlassian/perf-linting/no-expensive-split-replace -- Ignored via go/ees017 (to be fixed) tr.split(splitPos, splitDepth); splitPos += 2 * splitDepth; } splitPos += parent.child(_i2).nodeSize; } return tr; }