浏览代码

Changed: Support multiple file uploads in ImageUploadCommand.

Maciej Gołaszewski 7 年之前
父节点
当前提交
0c3cf7c947

+ 39 - 24
packages/ckeditor5-image/src/imageupload/imageuploadcommand.js

@@ -40,44 +40,59 @@ export default class ImageUploadCommand extends Command {
 	 *
 	 * @fires execute
 	 * @param {Object} options Options for the executed command.
-	 * @param {File} options.file The image file to upload.
-	 * @param {module:engine/model/position~Position} [options.insertAt] The position at which the image should be inserted.
+	 * @param {File|Array.<File>} options.file The image file or an array of image files to upload.
+	 * @param {module:engine/model/position~Position} [options.insertAt] The position at which the images should be inserted.
 	 * If the position is not specified, the image will be inserted into the current selection.
 	 * Note: You can use the {@link module:widget/utils~findOptimalInsertionPosition} function
 	 * to calculate (e.g. based on the current selection) a position which is more optimal from the UX perspective.
 	 */
 	execute( options ) {
 		const editor = this.editor;
-		const doc = editor.model.document;
-		const file = options.file;
-		const fileRepository = editor.plugins.get( FileRepository );
 
 		editor.model.change( writer => {
-			const loader = fileRepository.createLoader( file );
+			const filesToUpload = Array.isArray( options.file ) ? options.file : [ options.file ];
 
-			// Do not throw when upload adapter is not set. FileRepository will log an error anyway.
-			if ( !loader ) {
-				return;
+			// Reverse the order of items as the editor will place in reverse when using the same position.
+			for ( const file of filesToUpload.reverse() ) {
+				uploadImage( writer, editor, file, options.insertAt );
 			}
+		} );
+	}
+}
 
-			const imageElement = writer.createElement( 'image', {
-				uploadId: loader.id
-			} );
+// Handles uploading single file.
+//
+// @param {module:engine/model/writer~writer} writer
+// @param {module:core/editor/editor~Editor} editor
+// @param {File} file
+// @param {module:engine/model/position~Position} insertAt
+function uploadImage( writer, editor, file, insertAt ) {
+	const doc = editor.model.document;
+	const fileRepository = editor.plugins.get( FileRepository );
 
-			let insertAtSelection;
+	const loader = fileRepository.createLoader( file );
 
-			if ( options.insertAt ) {
-				insertAtSelection = new ModelSelection( [ new ModelRange( options.insertAt ) ] );
-			} else {
-				insertAtSelection = doc.selection;
-			}
+	// Do not throw when upload adapter is not set. FileRepository will log an error anyway.
+	if ( !loader ) {
+		return;
+	}
 
-			editor.model.insertContent( imageElement, insertAtSelection );
+	const imageElement = writer.createElement( 'image', {
+		uploadId: loader.id
+	} );
 
-			// Inserting an image might've failed due to schema regulations.
-			if ( imageElement.parent ) {
-				writer.setSelection( imageElement, 'on' );
-			}
-		} );
+	let insertAtSelection;
+
+	if ( insertAt ) {
+		insertAtSelection = new ModelSelection( [ new ModelRange( insertAt ) ] );
+	} else {
+		insertAtSelection = doc.selection;
+	}
+
+	editor.model.insertContent( imageElement, insertAtSelection );
+
+	// Inserting an image might've failed due to schema regulations.
+	if ( imageElement.parent ) {
+		writer.setSelection( imageElement, 'on' );
 	}
 }

+ 4 - 4
packages/ckeditor5-image/src/imageupload/imageuploadui.js

@@ -46,12 +46,12 @@ export default class ImageUploadUI extends Plugin {
 			view.buttonView.bind( 'isEnabled' ).to( command );
 
 			view.on( 'done', ( evt, files ) => {
-				for ( const file of Array.from( files ) ) {
+				const imagesToUpload = Array.from( files ).filter( isImageType );
+
+				if ( imagesToUpload.length ) {
 					const insertAt = findOptimalInsertionPosition( editor.model.document.selection );
 
-					if ( isImageType( file ) ) {
-						editor.execute( 'imageUpload', { file, insertAt } );
-					}
+					editor.execute( 'imageUpload', { file: imagesToUpload, insertAt } );
 				}
 			} );
 

+ 15 - 4
packages/ckeditor5-image/tests/imageupload/imageuploadui.js

@@ -99,7 +99,18 @@ describe( 'ImageUploadUI', () => {
 		button.fire( 'done', files );
 		sinon.assert.calledOnce( executeStub );
 		expect( executeStub.firstCall.args[ 0 ] ).to.equal( 'imageUpload' );
-		expect( executeStub.firstCall.args[ 1 ].file ).to.equal( files[ 0 ] );
+		expect( executeStub.firstCall.args[ 1 ].file ).to.deep.equal( files );
+	} );
+
+	it( 'should execute imageUpload command with multiple files', () => {
+		const executeStub = sinon.stub( editor, 'execute' );
+		const button = editor.ui.componentFactory.create( 'imageUpload' );
+		const files = [ createNativeFileMock(), createNativeFileMock(), createNativeFileMock() ];
+
+		button.fire( 'done', files );
+		sinon.assert.calledOnce( executeStub );
+		expect( executeStub.firstCall.args[ 0 ] ).to.equal( 'imageUpload' );
+		expect( executeStub.firstCall.args[ 1 ].file ).to.deep.equal( files );
 	} );
 
 	it( 'should optimize the insertion position', () => {
@@ -131,8 +142,8 @@ describe( 'ImageUploadUI', () => {
 
 		expect( getModelData( model ) ).to.equal(
 			'<paragraph>foo</paragraph>' +
-			`<image uploadId="${ id1 }" uploadStatus="reading"></image>` +
-			`[<image uploadId="${ id2 }" uploadStatus="reading"></image>]` +
+			`[<image uploadId="${ id1 }" uploadStatus="reading"></image>]` +
+			`<image uploadId="${ id2 }" uploadStatus="reading"></image>` +
 			'<paragraph>bar</paragraph>'
 		);
 	} );
@@ -160,6 +171,6 @@ describe( 'ImageUploadUI', () => {
 		button.fire( 'done', files );
 		sinon.assert.calledOnce( executeStub );
 		expect( executeStub.firstCall.args[ 0 ] ).to.equal( 'imageUpload' );
-		expect( executeStub.firstCall.args[ 1 ].file ).to.equal( files[ 0 ] );
+		expect( executeStub.firstCall.args[ 1 ].file ).to.deep.equal( [ files[ 0 ] ] );
 	} );
 } );