From c02de80168cdc492106efc34c6485a0002feb165 Mon Sep 17 00:00:00 2001 From: Yuriy Demidov Date: Mon, 27 May 2024 12:27:58 +0300 Subject: [PATCH] feat(Lists): add auto merging of adjacent list of same type (#241) --- src/extensions/markdown/Lists/index.ts | 3 ++ .../Lists/plugins/MergeListsPlugin.ts | 41 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/extensions/markdown/Lists/plugins/MergeListsPlugin.ts diff --git a/src/extensions/markdown/Lists/index.ts b/src/extensions/markdown/Lists/index.ts index 3f2ffc62..853dfa2c 100644 --- a/src/extensions/markdown/Lists/index.ts +++ b/src/extensions/markdown/Lists/index.ts @@ -9,6 +9,7 @@ import {actions} from './actions'; import {joinPrevList, liftIfCursorIsAtBeginningOfItem, toList} from './commands'; import {ListAction} from './const'; import {ListsInputRulesExtension, ListsInputRulesOptions} from './inputrules'; +import {mergeListsPlugin} from './plugins/MergeListsPlugin'; export {ListNode, blType, liType, olType} from './ListsSpecs'; @@ -48,6 +49,8 @@ export const Lists: ExtensionAuto = (builder, opts) => { builder.use(ListsInputRulesExtension, {bulletListInputRule: opts?.ulInputRules}); + builder.addPlugin(mergeListsPlugin); + builder .addAction(ListAction.ToBulletList, actions.toBulletList) .addAction(ListAction.ToOrderedList, actions.toOrderedList) diff --git a/src/extensions/markdown/Lists/plugins/MergeListsPlugin.ts b/src/extensions/markdown/Lists/plugins/MergeListsPlugin.ts new file mode 100644 index 00000000..81e53a8b --- /dev/null +++ b/src/extensions/markdown/Lists/plugins/MergeListsPlugin.ts @@ -0,0 +1,41 @@ +import {Plugin, Transaction} from 'prosemirror-state'; +import {findChildren, hasParentNode} from 'prosemirror-utils'; + +import {isListNode} from '../utils'; + +export const mergeListsPlugin = () => + new Plugin({ + appendTransaction(trs, oldState, newState) { + const docChanged = trs.some((tr) => tr.docChanged); + if (!docChanged) return null; + + const hasParentList = + hasParentNode(isListNode)(newState.selection) || + hasParentNode(isListNode)(oldState.selection); + if (!hasParentList) return null; + + const {tr} = newState; + const listNodes = findChildren(tr.doc, isListNode, true); + + mergeAdjacentNodesWithSameType(tr, listNodes); + + return tr.docChanged ? tr : null; + }, + }); + +function mergeAdjacentNodesWithSameType( + tr: Transaction, + nodes: ReturnType, +): void { + for (let i = 0; i < nodes.length - 1; i++) { + const current = nodes[i]; + const next = nodes[i + 1]; + + if ( + current.node.type === next.node.type && + current.pos + current.node.nodeSize === next.pos + ) { + tr.join(next.pos); + } + } +}