7
7
ImageInfo ,
8
8
ImageState ,
9
9
} from '../../../text-editor.types' ;
10
- import { Node } from 'prosemirror-model' ;
10
+ import { Node , Slice , Fragment } from 'prosemirror-model' ;
11
11
import { imageCache } from './node' ;
12
12
13
13
export const pluginKey = new PluginKey ( 'imageInserterPlugin' ) ;
@@ -27,8 +27,8 @@ export const createImageInserterPlugin = (
27
27
return new Plugin ( {
28
28
key : pluginKey ,
29
29
props : {
30
- handlePaste : ( view , event ) => {
31
- return processPasteEvent ( view , event ) ;
30
+ handlePaste : ( view , event , slice ) => {
31
+ return processPasteEvent ( view , event , slice ) ;
32
32
} ,
33
33
handleDOMEvents : {
34
34
imagePasted : ( _ , event ) => {
@@ -167,15 +167,74 @@ const createFailedThumbnailInserter =
167
167
dispatch ( tr ) ;
168
168
} ;
169
169
170
+ /**
171
+ * Check if a given ProseMirror node or fragment contains any image nodes.
172
+ * @param node - The ProseMirror node or fragment to check.
173
+ * @returns A boolean indicating whether the node contains any image nodes.
174
+ */
175
+ const isImageNode = ( node : Node | Fragment ) : boolean => {
176
+ if ( node instanceof Node ) {
177
+ if ( node . type . name === 'image' ) {
178
+ return true ;
179
+ }
180
+
181
+ let found = false ;
182
+ node . content . forEach ( ( child ) => {
183
+ if ( isImageNode ( child ) ) {
184
+ found = true ;
185
+ }
186
+ } ) ;
187
+
188
+ return found ;
189
+ } else if ( node instanceof Fragment ) {
190
+ let found = false ;
191
+ node . forEach ( ( child ) => {
192
+ if ( isImageNode ( child ) ) {
193
+ found = true ;
194
+ }
195
+ } ) ;
196
+
197
+ return found ;
198
+ }
199
+
200
+ return false ;
201
+ } ;
202
+
203
+ /**
204
+ * Filter out image nodes from a ProseMirror fragment.
205
+ * @param fragment - The ProseMirror fragment to filter.
206
+ * @returns A new fragment with image nodes removed.
207
+ */
208
+ const filterImageNodes = ( fragment : Fragment ) : Fragment => {
209
+ const filteredChildren : Node [ ] = [ ] ;
210
+
211
+ fragment . forEach ( ( child ) => {
212
+ if ( ! isImageNode ( child ) ) {
213
+ if ( child . content . size > 0 ) {
214
+ const filteredContent = filterImageNodes ( child . content ) ;
215
+ const newNode = child . copy ( filteredContent ) ;
216
+ filteredChildren . push ( newNode ) ;
217
+ } else {
218
+ filteredChildren . push ( child ) ;
219
+ }
220
+ }
221
+ } ) ;
222
+
223
+ return Fragment . fromArray ( filteredChildren ) ;
224
+ } ;
225
+
170
226
/**
171
227
* Process a paste event and trigger an imagePasted event if an image file is pasted.
228
+ * If an HTML image element is pasted, this image is filtered out from the slice content.
229
+ *
172
230
* @param view - The ProseMirror editor view.
173
231
* @param event - The paste event.
174
232
* @returns A boolean; True if an image file was pasted to prevent default paste behavior, otherwise false.
175
233
*/
176
234
const processPasteEvent = (
177
235
view : EditorView ,
178
236
event : ClipboardEvent ,
237
+ slice : Slice ,
179
238
) : boolean => {
180
239
const clipboardData = event . clipboardData ;
181
240
if ( ! clipboardData ) {
@@ -202,5 +261,19 @@ const processPasteEvent = (
202
261
}
203
262
}
204
263
264
+ const filteredSlice = new Slice (
265
+ filterImageNodes ( slice . content ) ,
266
+ slice . openStart ,
267
+ slice . openEnd ,
268
+ ) ;
269
+
270
+ if ( filteredSlice . content . childCount < slice . content . childCount ) {
271
+ const { state, dispatch } = view ;
272
+ const tr = state . tr . replaceSelection ( filteredSlice ) ;
273
+ dispatch ( tr ) ;
274
+
275
+ return true ;
276
+ }
277
+
205
278
return files . length > 0 ;
206
279
} ;
0 commit comments