Przeglądaj źródła

Merge pull request #94 from ckeditor/t/93

Internal: Improved conversion in disallowed context. Closes #93.
Piotr Jasiun 8 lat temu
rodzic
commit
d6bd81f1be

+ 22 - 4
packages/ckeditor5-list/src/converters.js

@@ -369,9 +369,18 @@ export function viewModelConverter( evt, data, conversionApi ) {
 		// `listItem`s created recursively should have bigger indent.
 		conversionStore.indent++;
 
-		writer.insert( listItem, data.modelCursor );
+		// Try to find allowed parent for list item.
+		const splitResult = conversionApi.splitToAllowedParent( listItem, data.modelCursor );
 
-		// Remember position after list item.
+		// When there is no allowed parent it means that list item cannot be converted at current model position
+		// and in any of position ancestors.
+		if ( !splitResult ) {
+			return;
+		}
+
+		writer.insert( listItem, splitResult.position );
+
+		// Remember position after list item, next list items will be inserted at this position.
 		let nextPosition = ModelPosition.createAfter( listItem );
 
 		// Check all children of the converted `<li>`.
@@ -382,7 +391,7 @@ export function viewModelConverter( evt, data, conversionApi ) {
 			if ( child.name == 'ul' || child.name == 'ol' ) {
 				nextPosition = conversionApi.convertItem( child, nextPosition ).modelCursor;
 			}
-			// If it was not a list it was a "regular" list item content. Just append it to `listItem`.
+			// If it was not a list it was a "regular" list item content. Just convert it to `listItem`.
 			else {
 				conversionApi.convertItem( child, ModelPosition.createAt( listItem, 'end' ) );
 			}
@@ -390,8 +399,17 @@ export function viewModelConverter( evt, data, conversionApi ) {
 
 		conversionStore.indent--;
 
+		// Result range starts before the first item and ends after the last.
 		data.modelRange = new ModelRange( data.modelCursor, nextPosition );
-		data.modelCursor = data.modelRange.end;
+
+		// When modelCursor parent had to be split to insert list item.
+		if ( splitResult.cursorParent ) {
+			// Then continue conversion in split element.
+			data.modelCursor = ModelPosition.createAt( splitResult.cursorParent );
+		} else {
+			// Otherwise continue conversion after last list item.
+			data.modelCursor = data.modelRange.end;
+		}
 	}
 }
 

+ 81 - 2
packages/ckeditor5-list/tests/listengine.js

@@ -14,8 +14,8 @@ import ModelText from '@ckeditor/ckeditor5-engine/src/model/text';
 import ViewPosition from '@ckeditor/ckeditor5-engine/src/view/position';
 import ViewContainerElement from '@ckeditor/ckeditor5-engine/src/view/containerelement';
 import ViewUIElement from '@ckeditor/ckeditor5-engine/src/view/uielement';
+import buildViewConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildviewconverter';
 
-import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
 import BoldEngine from '@ckeditor/ckeditor5-basic-styles/src/boldengine';
 import UndoEngine from '@ckeditor/ckeditor5-undo/src/undoengine';
 import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
@@ -33,7 +33,7 @@ describe( 'ListEngine', () => {
 	beforeEach( () => {
 		return VirtualTestEditor
 			.create( {
-				plugins: [ Clipboard, BoldEngine, Paragraph, ListEngine, UndoEngine, BlockQuoteEngine ]
+				plugins: [ Clipboard, BoldEngine, ListEngine, UndoEngine, BlockQuoteEngine ]
 			} )
 			.then( newEditor => {
 				editor = newEditor;
@@ -3476,6 +3476,85 @@ describe( 'ListEngine', () => {
 		} );
 	} );
 
+	describe( 'schema checking and parent splitting', () => {
+		beforeEach( () => {
+			// Since this part of test tests only view->model conversion editing pipeline is not necessary.
+			editor.editing.destroy();
+		} );
+
+		it( 'list should be not converted when modelCursor and its ancestors disallow to insert list', () => {
+			model.document.createRoot( '$title', 'title' );
+
+			model.schema.register( '$title', {
+				disallow: '$block',
+				allow: 'inline'
+			} );
+
+			editor.data.set( '<ul><li>foo</li></ul>', 'title' );
+
+			expect( getModelData( model, { rootName: 'title', withoutSelection: true } ) ).to.equal( '' );
+		} );
+
+		it( 'should split parent element when one of modelCursor ancestors allows to insert list - in the middle', () => {
+			buildViewConverter().for( editor.data.viewToModel ).fromElement( 'div' ).toElement( 'div' );
+			model.schema.register( 'div', { inheritAllFrom: '$block' } );
+
+			editor.setData(
+				'<div>' +
+					'abc' +
+					'<ul>' +
+						'<li>foo</li>' +
+					'</ul>' +
+					'def' +
+				'</div>'
+			);
+
+			expect( getModelData( model, { withoutSelection: true } ) ).to.equal(
+				'<div>abc</div>' +
+				'<listItem indent="0" type="bulleted">foo</listItem>' +
+				'<div>def</div>'
+			);
+		} );
+
+		it( 'should split parent element when one of modelCursor ancestors allows to insert list - at the end', () => {
+			buildViewConverter().for( editor.data.viewToModel ).fromElement( 'div' ).toElement( 'div' );
+			model.schema.register( 'div', { inheritAllFrom: '$block' } );
+
+			editor.setData(
+				'<div>' +
+					'abc' +
+					'<ul>' +
+						'<li>foo</li>' +
+					'</ul>' +
+				'</div>'
+			);
+
+			expect( getModelData( model, { withoutSelection: true } ) ).to.equal(
+				'<div>abc</div>' +
+				'<listItem indent="0" type="bulleted">foo</listItem>'
+			);
+		} );
+
+		it( 'should split parent element when one of modelCursor ancestors allows to insert list - at the beginning', () => {
+			buildViewConverter().for( editor.data.viewToModel ).fromElement( 'div' ).toElement( 'div' );
+			model.schema.register( 'div', { inheritAllFrom: '$block' } );
+
+			editor.setData(
+				'<div>' +
+					'<ul>' +
+					'<li>foo</li>' +
+					'</ul>' +
+					'def' +
+				'</div>'
+			);
+
+			expect( getModelData( model, { withoutSelection: true } ) ).to.equal(
+				'<listItem indent="0" type="bulleted">foo</listItem>' +
+				'<div>def</div>'
+			);
+		} );
+	} );
+
 	function getViewPosition( root, path ) {
 		let parent = root;