1
- import { Editor } from "@tiptap/core" ;
2
1
import { Node } from "prosemirror-model" ;
3
2
4
3
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor" ;
5
4
import {
5
+ Block ,
6
6
BlockIdentifier ,
7
7
BlockSchema ,
8
8
InlineContentSchema ,
9
9
PartialBlock ,
10
10
StyleSchema ,
11
11
} from "../../schema" ;
12
- import { blockToNode } from "../nodeConversions/nodeConversions" ;
12
+ import { blockToNode , nodeToBlock } from "../nodeConversions/nodeConversions" ;
13
13
import { getNodeById } from "../nodeUtil" ;
14
14
import { Transaction } from "prosemirror-state" ;
15
15
@@ -22,7 +22,7 @@ export function insertBlocks<
22
22
referenceBlock : BlockIdentifier ,
23
23
placement : "before" | "after" | "nested" = "before" ,
24
24
editor : BlockNoteEditor < BSchema , I , S >
25
- ) : void {
25
+ ) : Block < BSchema , I , S > [ ] {
26
26
const ttEditor = editor . _tiptapEditor ;
27
27
28
28
const id =
@@ -35,39 +35,53 @@ export function insertBlocks<
35
35
) ;
36
36
}
37
37
38
- let insertionPos = - 1 ;
39
-
40
38
const { node, posBeforeNode } = getNodeById ( id , ttEditor . state . doc ) ;
41
39
42
40
if ( placement === "before" ) {
43
- insertionPos = posBeforeNode ;
41
+ ttEditor . view . dispatch (
42
+ ttEditor . state . tr . insert ( posBeforeNode , nodesToInsert )
43
+ ) ;
44
44
}
45
45
46
46
if ( placement === "after" ) {
47
- insertionPos = posBeforeNode + node . nodeSize ;
47
+ ttEditor . view . dispatch (
48
+ ttEditor . state . tr . insert ( posBeforeNode + node . nodeSize , nodesToInsert )
49
+ ) ;
48
50
}
49
51
50
52
if ( placement === "nested" ) {
51
53
// Case if block doesn't already have children.
52
54
if ( node . childCount < 2 ) {
53
- insertionPos = posBeforeNode + node . firstChild ! . nodeSize + 1 ;
54
-
55
55
const blockGroupNode = ttEditor . state . schema . nodes [ "blockGroup" ] . create (
56
56
{ } ,
57
57
nodesToInsert
58
58
) ;
59
59
60
60
ttEditor . view . dispatch (
61
- ttEditor . state . tr . insert ( insertionPos , blockGroupNode )
61
+ ttEditor . state . tr . insert (
62
+ posBeforeNode + node . firstChild ! . nodeSize + 1 ,
63
+ blockGroupNode
64
+ )
62
65
) ;
63
-
64
- return ;
65
66
}
67
+ }
66
68
67
- insertionPos = posBeforeNode + node . firstChild ! . nodeSize + 2 ;
69
+ // Now that the `PartialBlock`s have been converted to nodes, we can
70
+ // re-convert them into full `Block`s.
71
+ const insertedBlocks : Block < BSchema , I , S > [ ] = [ ] ;
72
+ for ( const node of nodesToInsert ) {
73
+ insertedBlocks . push (
74
+ nodeToBlock (
75
+ node ,
76
+ editor . blockSchema ,
77
+ editor . inlineContentSchema ,
78
+ editor . styleSchema ,
79
+ editor . blockCache
80
+ )
81
+ ) ;
68
82
}
69
83
70
- ttEditor . view . dispatch ( ttEditor . state . tr . insert ( insertionPos , nodesToInsert ) ) ;
84
+ return insertedBlocks ;
71
85
}
72
86
73
87
export function updateBlock <
@@ -77,37 +91,56 @@ export function updateBlock<
77
91
> (
78
92
blockToUpdate : BlockIdentifier ,
79
93
update : PartialBlock < BSchema , I , S > ,
80
- editor : Editor
81
- ) {
94
+ editor : BlockNoteEditor < BSchema , I , S >
95
+ ) : Block < BSchema , I , S > {
96
+ const ttEditor = editor . _tiptapEditor ;
97
+
82
98
const id =
83
99
typeof blockToUpdate === "string" ? blockToUpdate : blockToUpdate . id ;
84
- const { posBeforeNode } = getNodeById ( id , editor . state . doc ) ;
100
+ const { posBeforeNode } = getNodeById ( id , ttEditor . state . doc ) ;
85
101
86
- editor . commands . BNUpdateBlock ( posBeforeNode + 1 , update ) ;
102
+ ttEditor . commands . BNUpdateBlock ( posBeforeNode + 1 , update ) ;
103
+
104
+ const blockContainerNode = ttEditor . state . doc
105
+ . resolve ( posBeforeNode + 1 )
106
+ . node ( ) ;
107
+
108
+ return nodeToBlock (
109
+ blockContainerNode ,
110
+ editor . blockSchema ,
111
+ editor . inlineContentSchema ,
112
+ editor . styleSchema ,
113
+ editor . blockCache
114
+ ) ;
87
115
}
88
116
89
- function removeBlocksWithCallback (
117
+ function removeBlocksWithCallback <
118
+ BSchema extends BlockSchema ,
119
+ I extends InlineContentSchema ,
120
+ S extends StyleSchema
121
+ > (
90
122
blocksToRemove : BlockIdentifier [ ] ,
91
- editor : Editor ,
123
+ editor : BlockNoteEditor < BSchema , I , S > ,
92
124
// Should return new removedSize.
93
125
callback ?: (
94
126
node : Node ,
95
127
pos : number ,
96
128
tr : Transaction ,
97
129
removedSize : number
98
130
) => number
99
- ) {
100
- const tr = editor . state . tr ;
131
+ ) : Block < BSchema , I , S > [ ] {
132
+ const ttEditor = editor . _tiptapEditor ;
133
+ const tr = ttEditor . state . tr ;
101
134
102
135
const idsOfBlocksToRemove = new Set < string > (
103
136
blocksToRemove . map ( ( block ) =>
104
137
typeof block === "string" ? block : block . id
105
138
)
106
139
) ;
107
-
140
+ const removedBlocks : Block < BSchema , I , S > [ ] = [ ] ;
108
141
let removedSize = 0 ;
109
142
110
- editor . state . doc . descendants ( ( node , pos ) => {
143
+ ttEditor . state . doc . descendants ( ( node , pos ) => {
111
144
// Skips traversing nodes after all target blocks have been removed.
112
145
if ( idsOfBlocksToRemove . size === 0 ) {
113
146
return false ;
@@ -121,10 +154,20 @@ function removeBlocksWithCallback(
121
154
return true ;
122
155
}
123
156
124
- removedSize = callback ?.( node , pos , tr , removedSize ) || removedSize ;
125
-
157
+ // Saves the block that is being deleted.
158
+ removedBlocks . push (
159
+ nodeToBlock (
160
+ node ,
161
+ editor . blockSchema ,
162
+ editor . inlineContentSchema ,
163
+ editor . styleSchema ,
164
+ editor . blockCache
165
+ )
166
+ ) ;
126
167
idsOfBlocksToRemove . delete ( node . attrs . id ) ;
127
168
169
+ // Removes the block and calculates the change in document size.
170
+ removedSize = callback ?.( node , pos , tr , removedSize ) || removedSize ;
128
171
const oldDocSize = tr . doc . nodeSize ;
129
172
tr . delete ( pos - removedSize - 1 , pos - removedSize + node . nodeSize + 1 ) ;
130
173
const newDocSize = tr . doc . nodeSize ;
@@ -133,6 +176,7 @@ function removeBlocksWithCallback(
133
176
return false ;
134
177
} ) ;
135
178
179
+ // Throws an error if now all blocks could be found.
136
180
if ( idsOfBlocksToRemove . size > 0 ) {
137
181
const notFoundIds = [ ...idsOfBlocksToRemove ] . join ( "\n" ) ;
138
182
@@ -142,14 +186,20 @@ function removeBlocksWithCallback(
142
186
) ;
143
187
}
144
188
145
- editor . view . dispatch ( tr ) ;
189
+ ttEditor . view . dispatch ( tr ) ;
190
+
191
+ return removedBlocks ;
146
192
}
147
193
148
- export function removeBlocks (
194
+ export function removeBlocks <
195
+ BSchema extends BlockSchema ,
196
+ I extends InlineContentSchema ,
197
+ S extends StyleSchema
198
+ > (
149
199
blocksToRemove : BlockIdentifier [ ] ,
150
- editor : Editor
151
- ) {
152
- removeBlocksWithCallback ( blocksToRemove , editor ) ;
200
+ editor : BlockNoteEditor < BSchema , I , S >
201
+ ) : Block < BSchema , I , S > [ ] {
202
+ return removeBlocksWithCallback ( blocksToRemove , editor ) ;
153
203
}
154
204
155
205
export function replaceBlocks <
@@ -160,24 +210,24 @@ export function replaceBlocks<
160
210
blocksToRemove : BlockIdentifier [ ] ,
161
211
blocksToInsert : PartialBlock < BSchema , I , S > [ ] ,
162
212
editor : BlockNoteEditor < BSchema , I , S >
163
- ) {
213
+ ) : {
214
+ insertedBlocks : Block < BSchema , I , S > [ ] ;
215
+ removedBlocks : Block < BSchema , I , S > [ ] ;
216
+ } {
164
217
const ttEditor = editor . _tiptapEditor ;
165
218
166
219
const nodesToInsert : Node [ ] = [ ] ;
167
- for ( const blockSpec of blocksToInsert ) {
168
- nodesToInsert . push (
169
- blockToNode ( blockSpec , ttEditor . schema , editor . styleSchema )
170
- ) ;
220
+ for ( const block of blocksToInsert ) {
221
+ nodesToInsert . push ( blockToNode ( block , ttEditor . schema , editor . styleSchema ) ) ;
171
222
}
172
223
173
224
const idOfFirstBlock =
174
225
typeof blocksToRemove [ 0 ] === "string"
175
226
? blocksToRemove [ 0 ]
176
227
: blocksToRemove [ 0 ] . id ;
177
-
178
- removeBlocksWithCallback (
228
+ const removedBlocks = removeBlocksWithCallback (
179
229
blocksToRemove ,
180
- ttEditor ,
230
+ editor ,
181
231
( node , pos , tr , removedSize ) => {
182
232
if ( node . attrs . id === idOfFirstBlock ) {
183
233
const oldDocSize = tr . doc . nodeSize ;
@@ -190,4 +240,21 @@ export function replaceBlocks<
190
240
return removedSize ;
191
241
}
192
242
) ;
243
+
244
+ // Now that the `PartialBlock`s have been converted to nodes, we can
245
+ // re-convert them into full `Block`s.
246
+ const insertedBlocks : Block < BSchema , I , S > [ ] = [ ] ;
247
+ for ( const node of nodesToInsert ) {
248
+ insertedBlocks . push (
249
+ nodeToBlock (
250
+ node ,
251
+ editor . blockSchema ,
252
+ editor . inlineContentSchema ,
253
+ editor . styleSchema ,
254
+ editor . blockCache
255
+ )
256
+ ) ;
257
+ }
258
+
259
+ return { insertedBlocks, removedBlocks } ;
193
260
}
0 commit comments