8
0
Просмотр исходного кода

Fix typing inside a text node with attributes at the beginning of a todo list item.

Maciej Gołaszewski 6 лет назад
Родитель
Сommit
21b8e1182f

+ 31 - 24
packages/ckeditor5-list/src/todolistconverters.js

@@ -9,7 +9,7 @@
 
 
 /* global document */
 /* global document */
 
 
-import { generateLiInUl, injectViewList, findInRange } from './utils';
+import { findInRange, generateLiInUl, injectViewList } from './utils';
 import createElement from '@ckeditor/ckeditor5-utils/src/dom/createelement';
 import createElement from '@ckeditor/ckeditor5-utils/src/dom/createelement';
 
 
 /**
 /**
@@ -71,27 +71,39 @@ export function modelViewInsertion( model, onCheckboxChecked ) {
  * @param {Object} data Additional information about the change.
  * @param {Object} data Additional information about the change.
  * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi Conversion interface.
  * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi Conversion interface.
  */
  */
-export function modelViewTextInsertion( evt, data, conversionApi ) {
-	const parent = data.range.start.parent;
+export function modelViewTextInsertion( view ) {
+	return ( evt, data, conversionApi ) => {
+		const parent = data.range.start.parent;
 
 
-	if ( parent.name != 'listItem' || parent.getAttribute( 'listType' ) != 'todo' ) {
-		return;
-	}
+		if ( parent.name != 'listItem' || parent.getAttribute( 'listType' ) != 'todo' ) {
+			return;
+		}
 
 
-	if ( !conversionApi.consumable.consume( data.item, 'insert' ) ) {
-		return;
-	}
+		if ( !conversionApi.consumable.consume( data.item, 'insert' ) ) {
+			return;
+		}
 
 
-	const viewWriter = conversionApi.writer;
-	const viewPosition = conversionApi.mapper.toViewPosition( data.range.start );
-	const viewText = viewWriter.createText( data.item.data );
+		const viewWriter = conversionApi.writer;
+		const viewPosition = conversionApi.mapper.toViewPosition( data.range.start );
+		const viewText = viewWriter.createText( data.item.data );
+
+		// Be sure text is created after the UIElement, so if it is a first text node inside a `listItem` element
+		// it has to be moved after the first node in the view list item.
+		//
+		// model: <listItem listtype="todo">[foo]</listItem>
+		// view: <li>^<checkbox/></li> -> <li><checkbox/>foo</li>
+		const textInsertionPosition = viewPosition.offset ? viewPosition : viewPosition.getShiftedBy( 1 );
+
+		const label = findLabel( viewPosition.parent, view );
+
+		if ( label && label.parent !== viewPosition.parent && viewPosition.offset === 0 ) {
+			viewWriter.insert( view.createPositionAfter( label ), viewText );
 
 
-	// Be sure text is created after the UIElement, so if it is a first text node inside a `listItem` element
-	// it has to be moved after the first node in the view list item.
-	//
-	// model: <listItem listtype="todo">[foo]</listItem>
-	// view: <li>^<checkbox/></li> -> <li><checkbox/>foo</li>
-	viewWriter.insert( viewPosition.offset ? viewPosition : viewPosition.getShiftedBy( 1 ), viewText );
+			return;
+		}
+
+		viewWriter.insert( textInsertionPosition, viewText );
+	};
 }
 }
 
 
 /**
 /**
@@ -246,12 +258,7 @@ export function modelViewChangeType( onCheckedChange, view ) {
 			viewWriter.insert( viewWriter.createPositionAt( viewItem, 0 ), checkmarkElement );
 			viewWriter.insert( viewWriter.createPositionAt( viewItem, 0 ), checkmarkElement );
 		} else if ( data.attributeOldValue == 'todo' ) {
 		} else if ( data.attributeOldValue == 'todo' ) {
 			viewWriter.removeClass( 'todo-list', viewItem.parent );
 			viewWriter.removeClass( 'todo-list', viewItem.parent );
-
-			const label = findLabel( viewItem, view );
-
-			if ( label ) {
-				viewWriter.remove( label );
-			}
+			viewWriter.remove( findLabel( viewItem, view ) );
 		}
 		}
 	};
 	};
 }
 }

+ 1 - 1
packages/ckeditor5-list/src/todolistediting.js

@@ -73,7 +73,7 @@ export default class TodoListEditing extends Plugin {
 			modelViewInsertion( model, listItem => this._handleCheckmarkChange( listItem ) ),
 			modelViewInsertion( model, listItem => this._handleCheckmarkChange( listItem ) ),
 			{ priority: 'high' }
 			{ priority: 'high' }
 		);
 		);
-		editing.downcastDispatcher.on( 'insert:$text', modelViewTextInsertion, { priority: 'high' } );
+		editing.downcastDispatcher.on( 'insert:$text', modelViewTextInsertion( editing.view ), { priority: 'high' } );
 		data.downcastDispatcher.on( 'insert:listItem', dataModelViewInsertion( model ), { priority: 'high' } );
 		data.downcastDispatcher.on( 'insert:listItem', dataModelViewInsertion( model ), { priority: 'high' } );
 		data.downcastDispatcher.on( 'insert:$text', dataModelViewTextInsertion, { priority: 'high' } );
 		data.downcastDispatcher.on( 'insert:$text', dataModelViewTextInsertion, { priority: 'high' } );
 
 

+ 44 - 0
packages/ckeditor5-list/tests/todolistediting.js

@@ -411,6 +411,50 @@ describe( 'TodoListEditing', () => {
 			model.change( writer => writer.setAttribute( 'todoListChecked', true, modelRoot.getChild( 0 ) ) );
 			model.change( writer => writer.setAttribute( 'todoListChecked', true, modelRoot.getChild( 0 ) ) );
 			expect( getViewData( view ) ).to.equal( '<test class="checked">{}Foo</test>' );
 			expect( getViewData( view ) ).to.equal( '<test class="checked">{}Foo</test>' );
 		} );
 		} );
+
+		it( 'should properly handle typing inside text node with attribute', () => {
+			setModelData( model, '<listItem listType="todo" listIndent="0"><$text bold="true">[]foo</$text></listItem>' );
+
+			editor.execute( 'input', { text: 'b' } );
+
+			expect( getModelData( model ) ).to.equal(
+				'<listItem listIndent="0" listType="todo"><$text bold="true">b[]foo</$text></listItem>'
+			);
+
+			expect( getViewData( view ) ).to.equal(
+				'<ul class="todo-list">' +
+					'<li>' +
+						'<strong>' +
+							'<label class="todo-list__checkmark" contenteditable="false"></label>b{}foo' +
+						'</strong>' +
+					'</li>' +
+				'</ul>'
+			);
+		} );
+
+		it( 'should properly handle typing inside text node with many attributes', () => {
+			setModelData( model,
+				'<listItem listType="todo" listIndent="0"><$text bold="true" linkHref="foo">[]foo</$text></listItem>'
+			);
+
+			editor.execute( 'input', { text: 'b' } );
+
+			expect( getModelData( model ) ).to.equal(
+				'<listItem listIndent="0" listType="todo"><$text bold="true" linkHref="foo">b[]foo</$text></listItem>'
+			);
+
+			expect( getViewData( view ) ).to.equal(
+				'<ul class="todo-list">' +
+					'<li>' +
+						'<a class="ck-link_selected" href="foo">' +
+							'<strong>' +
+								'<label class="todo-list__checkmark" contenteditable="false"></label>b{}foo' +
+							'</strong>' +
+						'</a>' +
+					'</li>' +
+				'</ul>'
+			);
+		} );
 	} );
 	} );
 
 
 	describe( 'data pipeline m -> v', () => {
 	describe( 'data pipeline m -> v', () => {