From 67c230025cea5c763d7754e7805c1b4783c23db7 Mon Sep 17 00:00:00 2001 From: Yuriy Demidov Date: Thu, 15 Feb 2024 18:50:06 +0300 Subject: [PATCH] feat(YfmCut): improve the behavior when creating yfm-cut (#191) - create an open yfm-cut - move cursor to title of created yfm-cut - move nodes between selections to content of create yfm-cut --- src/extensions/yfm/YfmCut/actions/toYfmCut.ts | 58 ++++++++++++++----- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/src/extensions/yfm/YfmCut/actions/toYfmCut.ts b/src/extensions/yfm/YfmCut/actions/toYfmCut.ts index 18c23f82..db6b7c9e 100644 --- a/src/extensions/yfm/YfmCut/actions/toYfmCut.ts +++ b/src/extensions/yfm/YfmCut/actions/toYfmCut.ts @@ -1,32 +1,62 @@ -import type {Node, Schema} from 'prosemirror-model'; -import type {Command} from 'prosemirror-state'; -import {findParentNodeOfType} from 'prosemirror-utils'; +import type {Fragment, Node, Schema} from 'prosemirror-model'; +import {Command, TextSelection} from 'prosemirror-state'; +import {findParentNodeClosestToPos} from 'prosemirror-utils'; import type {ActionSpec} from '../../../../core'; import {cutContentType, cutTitleType, cutType} from '../const'; -const createYfmCutNode = (schema: Schema) => (content?: Node | Node[]) => { - return cutType(schema).create(null, [ +const createYfmCutNode = (schema: Schema) => (content?: Node | Node[] | Fragment) => { + return cutType(schema).create({class: 'yfm-cut open'}, [ cutTitleType(schema).create(null), cutContentType(schema).create(null, content), ]); }; export const createYfmCut: Command = (state, dispatch) => { - const {schema} = state; + const {schema, selection: sel} = state; - const parent = findParentNodeOfType(schema.nodes.paragraph)(state.selection); - if (parent) { - if (dispatch) { - const yfmCut = createYfmCutNode(schema)(parent.node); + const textblock = findParentNodeClosestToPos( + sel.$from, + (node) => node.isTextblock && !node.type.spec.complex, + ); - dispatch?.(state.tr.replaceWith(parent.pos, parent.pos + parent.node.nodeSize, yfmCut)); - } + if (!textblock) return false; - return true; + if (dispatch) { + const sliceFromPos = textblock.pos; + let sliceToPos = sliceFromPos + textblock.node.nodeSize; + + const tbIndex = sel.$from.index(textblock.depth - 1); + const tbParent = sel.$from.node(textblock.depth - 1); + const tbParentStartPos = sel.$from.start(textblock.depth - 1); + + let flag = false; + // find appropriate sliceToPos + tbParent.forEach((node, offset, index) => { + if (index < tbIndex || flag) return; + if (node.type.spec.complex && node.type.spec.complex !== 'root') { + flag = true; + return; + } + + const absoluteAfterPos = tbParentStartPos + offset + node.nodeSize; + sliceToPos = absoluteAfterPos; + + if (sel.to < sliceToPos) flag = true; + }); + + const tr = state.tr; + tr.replaceWith( + sliceFromPos, + sliceToPos, + createYfmCutNode(schema)(tr.doc.slice(sliceFromPos, sliceToPos, false).content), + ); + // set selection to start of cut title + tr.setSelection(TextSelection.create(tr.doc, sliceFromPos + 2)); + dispatch(tr.scrollIntoView()); } - return false; + return true; }; export const toYfmCut: ActionSpec = {