Ver código fonte

Refactoring, added FormatsCommand instance to editor.

Szymon Kupś 9 anos atrás
pai
commit
16d5c9fa1f

+ 33 - 26
packages/ckeditor5-heading/src/formatscommand.js

@@ -5,49 +5,56 @@
 
 'use strict';
 
-import Command from '../command.js';
+import Command from '../command/command.js';
 
 export default class FormatsCommand extends Command {
-	constructor( editor ) {
+	constructor( editor, formats ) {
 		super( editor );
 
-		this.set( 'value', 'paragraph' );
+		this.formats = formats;
+		this.defaultFormat = this.formats[ 0 ];
+
+		this.set( 'value', this.formats[ 0 ] );
 
 		this.listenTo( editor.document.selection, 'change', () => {
 			const position = editor.document.selection.getFirstPosition();
 			const parent = position.parent;
-
-			switch ( parent.name ) {
-				case 'h2':
-					this.value = 'heading1';
-					break;
-
-				case 'h3':
-					this.value = 'heading2';
-					break;
-
-				case 'h4':
-					this.value = 'heading3';
-					break;
-
-				default:
-					this.value = 'paragraph';
-			}
+			this.value = parent.name;
 		} );
 	}
 
 	_doExecute( forceValue ) {
 		const document = this.editor.document;
 		const selection = document.selection;
-		const value = ( forceValue === undefined ) ? 'paragraph' : forceValue;
-		let element;
+		const newValue = ( forceValue === undefined ) ? 'paragraph' : forceValue;
+		const position = selection.getFirstPosition();
+		const elements = [];
+		let remove = false;
+
+		// If start position is same as new value - we are toggling already applied format back to default one.
+		if ( newValue === position.parent.name ) {
+			remove = true;
+		}
 
 		if ( selection.isCollapsed ) {
-			const position = selection.getFirstPosition();
-			element = position.parent;
+			elements.push( position.parent );
 		}
 
-		const batch = document.batch();
-		batch.rename( value, element );
+		document.enqueueChanges( () => {
+			const batch = document.batch();
+
+			for ( let element of elements ) {
+				// When removing applied format.
+				if ( remove ) {
+					if ( element.name === newValue ) {
+						batch.rename( this.defaultFormat.id, element );
+					}
+				}
+				// When applying new format.
+				else {
+					batch.rename( newValue, element );
+				}
+			}
+		} );
 	}
 }

+ 29 - 32
packages/ckeditor5-heading/src/formatsengine.js

@@ -9,6 +9,14 @@ import Feature from '../feature.js';
 import BuildModelConverterFor from '../engine/conversion/model-converter-builder.js';
 import BuildViewConverterFor from '../engine/conversion/view-converter-builder.js';
 import Paragraph from '../paragraph/paragraph.js';
+import FormatsCommand from './formatscommand.js';
+
+const formats = [
+	{ id: 'paragraph', viewElement: 'p' },
+	{ id: 'heading1', viewElement: 'h2' },
+	{ id: 'heading2', viewElement: 'h3' },
+	{ id: 'heading3', viewElement: 'h4' }
+];
 
 export default class FormatsEngine extends Feature {
 	static get requires() {
@@ -22,37 +30,26 @@ export default class FormatsEngine extends Feature {
 		const data = editor.data;
 		const editing = editor.editing;
 
-		// Schema.
-		schema.registerItem( 'heading1', '$block' );
-		schema.registerItem( 'heading2', '$block' );
-		schema.registerItem( 'heading3', '$block' );
-
-		// Build converter from model to view for data pipeline.
-		BuildModelConverterFor( data.modelToView, editing.modelToView )
-			.fromElement( 'heading1' )
-			.toElement( 'h2' );
-
-		BuildModelConverterFor( data.modelToView )
-			.fromElement( 'heading2' )
-			.toElement( 'h3' );
-
-		BuildModelConverterFor( data.modelToView )
-			.fromElement( 'heading3' )
-			.toElement( 'h4' );
-
-		// Build converter from view to model for data pipeline.
-		BuildViewConverterFor( data.viewToModel )
-			.fromElement( 'h2' )
-			.toElement( 'heading1' );
-
-		BuildViewConverterFor( data.viewToModel )
-			.fromElement( 'h3' )
-			.toElement( 'heading2' );
-
-		BuildViewConverterFor( data.viewToModel )
-			.fromElement( 'h4' )
-			.toElement( 'heading3' );
-
-		// TODO: register command.
+		for ( let format of formats ) {
+			// Skip paragraph - it is defined in required Paragraph feature.
+			if ( format.id !== 'paragraph' ) {
+				// Schema.
+				schema.registerItem( format.id, '$block' );
+
+				// Build converter from model to view for data pipeline.
+				BuildModelConverterFor( data.modelToView, editing.modelToView )
+					.fromElement( format.id )
+					.toElement( format.viewElement );
+
+				// Build converter from view to model for data pipeline.
+				BuildViewConverterFor( data.viewToModel )
+					.fromElement( format.viewElement )
+					.toElement( format.id );
+			}
+		}
+
+		// Register command.
+		const command = new FormatsCommand( editor, formats );
+		editor.commands.set( 'format', command );
 	}
 }

+ 81 - 0
packages/ckeditor5-heading/tests/formatscommand.js

@@ -4,3 +4,84 @@
  */
 
 'use strict';
+
+import StandardEditor from '/ckeditor5/editor/standardeditor.js';
+import FormatsCommand from '/ckeditor5/formats/formatscommand.js';
+import Range from '/ckeditor5/engine/model/range.js';
+import { setData, getData } from '/tests/engine/_utils/model.js';
+
+const formats = [
+	{ id: 'paragraph', viewElement: 'p' },
+	{ id: 'heading1', viewElement: 'h2' },
+	{ id: 'heading2', viewElement: 'h3' },
+	{ id: 'heading3', viewElement: 'h4' }
+];
+
+describe( 'FormatsCommand', () => {
+	let editor, document, command, root;
+
+	beforeEach( () => {
+		editor = new StandardEditor( null );
+		document = editor.document;
+		command = new FormatsCommand( editor, formats );
+		const schema = document.schema;
+
+		for ( let format of formats ) {
+			schema.registerItem( format.id, '$block' );
+		}
+
+		root = document.createRoot();
+	} );
+
+	afterEach( () => {
+		command.destroy();
+	} );
+
+	describe( 'value', () => {
+		for ( let format of formats ) {
+			test( format );
+		}
+
+		function test( format ) {
+			it( `equals ${ format.id } when collapsed selection is placed inside ${ format.id } element`, () => {
+				setData( document, `<${ format.id }>foobar</${ format.id }>` );
+				const element = root.getChild( 0 );
+				document.selection.addRange( Range.createFromParentsAndOffsets( element, 3, element, 3 ) );
+
+				expect( command.value ).to.equal( format.id );
+			} );
+		}
+	} );
+
+	describe( '_doExecute', () => {
+		let convertTo = formats[ formats.length - 1 ];
+
+		for ( let format of formats ) {
+			test( format, convertTo );
+			convertTo = format;
+		}
+
+		it( 'uses paragraph as default value', () => {
+			setData( document, '<heading1>foo<selection />bar</heading1>' );
+			command._doExecute();
+
+			expect( getData( document ) ).to.equal( '<paragraph>foo<selection />bar</paragraph>' );
+		} );
+
+		it( 'converts to default format when executed with already applied format', () => {
+			setData( document, '<heading1>foo<selection />bar</heading1>' );
+			command._doExecute( 'heading1' );
+
+			expect( getData( document ) ).to.equal( '<paragraph>foo<selection />bar</paragraph>' );
+		} );
+
+		function test( from, to ) {
+			it( `converts ${ from.id } to ${ to.id } on collapsed selection`, () => {
+				setData( document, `<${ from.id }>foo<selection />bar</${ from.id }>` );
+				command._doExecute( to.id );
+
+				expect( getData( document ) ).to.equal( `<${ to.id }>foo<selection />bar</${ to.id }>` );
+			} );
+		}
+	} );
+} );

+ 31 - 25
packages/ckeditor5-heading/tests/formatsengine.js

@@ -7,20 +7,19 @@
 
 import FormatsEngine from '/ckeditor5/formats/formatsengine.js';
 import Paragraph from '/ckeditor5/paragraph/paragraph.js';
-import Editor from '/ckeditor5/editor.js';
-import StandardCreator from '/ckeditor5/creator/standardcreator.js';
+import StandardEditor from '/ckeditor5/editor/standardeditor.js';
+import FormatsCommand from '/ckeditor5/formats/formatscommand.js';
 import { getData } from '/tests/engine/_utils/model.js';
 
 describe( 'FormatsEngine', () => {
 	let editor, document;
 
 	beforeEach( () => {
-		editor = new Editor( null, {
-			creator: StandardCreator,
+		editor = new StandardEditor( null, {
 			features: [ FormatsEngine ]
 		} );
 
-		return editor.init().then( () => {
+		return editor.initPlugins().then( () => {
 			document = editor.document;
 			document.createRoot( 'main' );
 		} );
@@ -49,24 +48,31 @@ describe( 'FormatsEngine', () => {
 		expect( document.schema.check( { name: '$inline', inside: 'heading3' } ) ).to.be.true;
 	} );
 
-	// it( 'should convert heading1', () => {
-	// 	editor.setData( '<h2>foobar</h2>' );
-	//
-	// 	expect( getData( document, { withoutSelection: true } ) ).to.equal( '<heading1>foobar</heading1>' );
-	// 	expect( editor.getData() ).to.equal( '<h2>foobar</h2>' );
-	// } );
-
-	// it( 'should convert heading2', () => {
-	// 	editor.setData( '<h3>foobar</h3>' );
-	//
-	// 	expect( getData( document, { withoutSelection: true } ) ).to.equal( '<heading2>foobar</heading2>' );
-	// 	expect( editor.getData() ).to.equal( '<h3>foobar</h3>' );
-	// } );
-	//
-	// it( 'should convert heading3', () => {
-	// 	editor.setData( '<h4>foobar</h4>' );
-	//
-	// 	expect( getData( document, { withoutSelection: true } ) ).to.equal( '<heading3>foobar</heading3>' );
-	// 	expect( editor.getData() ).to.equal( '<h4>foobar</h4>' );
-	// } );
+	it( 'should register format command', () => {
+		expect( editor.commands.has( 'format' ) ).to.be.true;
+		const command = editor.commands.get( 'format' );
+
+		expect( command ).to.be.instanceOf( FormatsCommand );
+	} );
+
+	it( 'should convert heading1', () => {
+		editor.setData( '<h2>foobar</h2>' );
+
+		expect( getData( document, { withoutSelection: true } ) ).to.equal( '<heading1>foobar</heading1>' );
+		expect( editor.getData() ).to.equal( '<h2>foobar</h2>' );
+	} );
+
+	it( 'should convert heading2', () => {
+		editor.setData( '<h3>foobar</h3>' );
+
+		expect( getData( document, { withoutSelection: true } ) ).to.equal( '<heading2>foobar</heading2>' );
+		expect( editor.getData() ).to.equal( '<h3>foobar</h3>' );
+	} );
+
+	it( 'should convert heading3', () => {
+		editor.setData( '<h4>foobar</h4>' );
+
+		expect( getData( document, { withoutSelection: true } ) ).to.equal( '<heading3>foobar</heading3>' );
+		expect( editor.getData() ).to.equal( '<h4>foobar</h4>' );
+	} );
 } );