1- import { ResolvedPos , type Node } from "prosemirror-model" ;
2- import { TextSelection , type Transaction } from "prosemirror-state" ;
1+ import { Slice , type Node } from "prosemirror-model" ;
2+ import { type Transaction } from "prosemirror-state" ;
33import { ReplaceAroundStep } from "prosemirror-transform" ;
44import type { Block , PartialBlock } from "../../../../blocks/defaultBlocks.js" ;
55import type {
@@ -8,158 +8,9 @@ import type {
88 InlineContentSchema ,
99 StyleSchema ,
1010} from "../../../../schema/index.js" ;
11- import { getBlockInfoFromResolvedPos } from "../../../getBlockInfoFromPos.js" ;
1211import { blockToNode } from "../../../nodeConversions/blockToNode.js" ;
1312import { nodeToBlock } from "../../../nodeConversions/nodeToBlock.js" ;
1413import { getPmSchema } from "../../../pmUtil.js" ;
15- import {
16- getParentBlockInfo ,
17- getPrevBlockInfo ,
18- } from "../mergeBlocks/mergeBlocks.js" ;
19-
20- // TODO: Where should this function go?
21- /**
22- * Moves the first block in a column to the previous/next column and handles
23- * all necessary collapsing of `column`/`columnList` nodes. Only moves the
24- * block to the start of the next column if it's in the first column.
25- * Otherwise, moves the block to the end of the previous column.
26- * @param tr The transaction to apply changes to.
27- * @param blockBeforePos The position just before the first block in the column.
28- * @returns The position just before the block, after it's moved.
29- */
30- export function moveFirstBlockInColumn (
31- tr : Transaction ,
32- blockBeforePos : ResolvedPos ,
33- ) : ResolvedPos {
34- const blockInfo = getBlockInfoFromResolvedPos ( blockBeforePos ) ;
35- if ( ! blockInfo . isBlockContainer ) {
36- throw new Error (
37- "Invalid blockBeforePos: does not point to blockContainer node." ,
38- ) ;
39- }
40-
41- const prevBlockInfo = getPrevBlockInfo ( tr . doc , blockInfo . bnBlock . beforePos ) ;
42- if ( prevBlockInfo ) {
43- throw new Error (
44- "Invalid blockBeforePos: does not point to first blockContainer node in column." ,
45- ) ;
46- }
47-
48- const parentBlockInfo = getParentBlockInfo (
49- tr . doc ,
50- blockInfo . bnBlock . beforePos ,
51- ) ;
52- if ( parentBlockInfo ?. blockNoteType !== "column" ) {
53- throw new Error (
54- "Invalid blockBeforePos: blockContainer node is not child of column." ,
55- ) ;
56- }
57-
58- const column = parentBlockInfo ;
59- const columnList = getParentBlockInfo ( tr . doc , column . bnBlock . beforePos ) ;
60- if ( columnList ?. blockNoteType !== "columnList" ) {
61- throw new Error (
62- "Invalid blockBeforePos: blockContainer node is child of column, but column is not child of columnList node." ,
63- ) ;
64- }
65-
66- const shouldRemoveColumn = column . childContainer ! . node . childCount === 1 ;
67-
68- const shouldRemoveColumnList =
69- shouldRemoveColumn && columnList . childContainer ! . node . childCount === 2 ;
70-
71- const isFirstColumn =
72- columnList . childContainer ! . node . firstChild === column . bnBlock . node ;
73-
74- const blockToMove = tr . doc . slice (
75- blockInfo . bnBlock . beforePos ,
76- blockInfo . bnBlock . afterPos ,
77- false ,
78- ) ;
79-
80- /*
81- There are 3 different cases:
82- a) remove entire column list (if no columns would be remaining)
83- b) remove just a column (if no blocks inside a column would be remaining)
84- c) keep columns (if there are blocks remaining inside a column)
85-
86- Each of these 3 cases has 2 sub-cases, depending on whether the backspace happens at the start of the first (most-left) column,
87- or at the start of a non-first column.
88- */
89- if ( shouldRemoveColumnList ) {
90- if ( isFirstColumn ) {
91- tr . step (
92- new ReplaceAroundStep (
93- // replace entire column list
94- columnList . bnBlock . beforePos ,
95- columnList . bnBlock . afterPos ,
96- // select content of remaining column:
97- column . bnBlock . afterPos + 1 ,
98- columnList . bnBlock . afterPos - 2 ,
99- blockToMove ,
100- blockToMove . size , // append existing content to blockToMove
101- false ,
102- ) ,
103- ) ;
104- const pos = tr . doc . resolve ( columnList . bnBlock . beforePos ) ;
105- tr . setSelection ( TextSelection . between ( pos , pos ) ) ;
106-
107- return pos ;
108- } else {
109- // replaces the column list with the blockToMove slice, prepended with the content of the remaining column
110- tr . step (
111- new ReplaceAroundStep (
112- // replace entire column list
113- columnList . bnBlock . beforePos ,
114- columnList . bnBlock . afterPos ,
115- // select content of existing column:
116- columnList . bnBlock . beforePos + 2 ,
117- column . bnBlock . beforePos - 1 ,
118- blockToMove ,
119- 0 , // prepend existing content to blockToMove
120- false ,
121- ) ,
122- ) ;
123- const pos = tr . doc . resolve ( tr . mapping . map ( column . bnBlock . beforePos - 1 ) ) ;
124- tr . setSelection ( TextSelection . between ( pos , pos ) ) ;
125-
126- return pos ;
127- }
128- } else if ( shouldRemoveColumn ) {
129- if ( isFirstColumn ) {
130- // delete column
131- tr . delete ( column . bnBlock . beforePos , column . bnBlock . afterPos ) ;
132-
133- // move before columnlist
134- tr . insert ( columnList . bnBlock . beforePos , blockToMove . content ) ;
135-
136- const pos = tr . doc . resolve ( columnList . bnBlock . beforePos ) ;
137- tr . setSelection ( TextSelection . between ( pos , pos ) ) ;
138-
139- return pos ;
140- } else {
141- // just delete the </column><column> closing and opening tags to merge the columns
142- tr . delete ( column . bnBlock . beforePos - 1 , column . bnBlock . beforePos + 1 ) ;
143- const pos = tr . doc . resolve ( column . bnBlock . beforePos - 1 ) ;
144-
145- return pos ;
146- }
147- } else {
148- // delete block
149- tr . delete ( blockInfo . bnBlock . beforePos , blockInfo . bnBlock . afterPos ) ;
150- if ( isFirstColumn ) {
151- // move before columnlist
152- tr . insert ( columnList . bnBlock . beforePos - 1 , blockToMove . content ) ;
153- } else {
154- // append block to previous column
155- tr . insert ( column . bnBlock . beforePos - 1 , blockToMove . content ) ;
156- }
157- const pos = tr . doc . resolve ( column . bnBlock . beforePos - 1 ) ;
158- tr . setSelection ( TextSelection . between ( pos , pos ) ) ;
159-
160- return pos ;
161- }
162- }
16314
16415/**
16516 * Checks if a `column` node is empty, i.e. if it has only a single empty
@@ -258,9 +109,11 @@ export function fixColumnList(tr: Transaction, columnListPos: number) {
258109 const firstColumnBeforePos = columnListPos + 1 ;
259110 const $firstColumnBeforePos = tr . doc . resolve ( firstColumnBeforePos ) ;
260111 const firstColumn = $firstColumnBeforePos . nodeAfter ;
112+
261113 const lastColumnAfterPos = columnListPos + columnList . nodeSize - 1 ;
262114 const $lastColumnAfterPos = tr . doc . resolve ( lastColumnAfterPos ) ;
263115 const lastColumn = $lastColumnAfterPos . nodeBefore ;
116+
264117 if ( ! firstColumn || ! lastColumn ) {
265118 throw new Error ( "Invalid columnList: does not have child node." ) ;
266119 }
@@ -269,32 +122,47 @@ export function fixColumnList(tr: Transaction, columnListPos: number) {
269122 const lastColumnEmpty = isEmptyColumn ( lastColumn ) ;
270123
271124 if ( firstColumnEmpty && lastColumnEmpty ) {
125+ // Removes `columnList`
272126 tr . delete ( columnListPos , columnListPos + columnList . nodeSize ) ;
273127
274128 return ;
275129 }
276130
277131 if ( firstColumnEmpty ) {
278- const lastColumnContent = tr . doc . slice (
279- lastColumnAfterPos - lastColumn . nodeSize + 1 ,
280- lastColumnAfterPos - 1 ,
132+ tr . step (
133+ new ReplaceAroundStep (
134+ // Replaces `columnList`.
135+ columnListPos ,
136+ columnListPos + columnList . nodeSize ,
137+ // Replaces with content of last `column`.
138+ lastColumnAfterPos - lastColumn . nodeSize + 1 ,
139+ lastColumnAfterPos - 1 ,
140+ // Doesn't append anything.
141+ Slice . empty ,
142+ 0 ,
143+ false ,
144+ ) ,
281145 ) ;
282146
283- tr . delete ( columnListPos , columnListPos + columnList . nodeSize ) ;
284- tr . insert ( columnListPos , lastColumnContent . content ) ;
285-
286147 return ;
287148 }
288149
289150 if ( lastColumnEmpty ) {
290- const firstColumnContent = tr . doc . slice (
291- firstColumnBeforePos + 1 ,
292- firstColumnBeforePos + firstColumn . nodeSize - 1 ,
151+ tr . step (
152+ new ReplaceAroundStep (
153+ // Replaces `columnList`.
154+ columnListPos ,
155+ columnListPos + columnList . nodeSize ,
156+ // Replaces with content of first `column`.
157+ firstColumnBeforePos + 1 ,
158+ firstColumnBeforePos + firstColumn . nodeSize - 1 ,
159+ // Doesn't append anything.
160+ Slice . empty ,
161+ 0 ,
162+ false ,
163+ ) ,
293164 ) ;
294165
295- tr . delete ( columnListPos , columnListPos + columnList . nodeSize ) ;
296- tr . insert ( columnListPos , firstColumnContent . content ) ;
297-
298166 return ;
299167 }
300168}
0 commit comments