Parcourir la source

Handle todo list checkbox by fixing modelToViewPosition mapping.

Maciej Gołaszewski il y a 6 ans
Parent
commit
6d2afa2d5e

+ 2 - 51
packages/ckeditor5-list/src/todolistconverters.js

@@ -60,53 +60,6 @@ export function modelViewInsertion( model, onCheckboxChecked ) {
 }
 
 /**
- * A model-to-view converter for the model `$text` element inside a to-do list item.
- *
- * It takes care of creating text after the {@link module:engine/view/uielement~UIElement checkbox UI element}.
- *
- * It is used by {@link module:engine/controller/editingcontroller~EditingController}.
- *
- * @see module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:insert
- * @param {module:utils/eventinfo~EventInfo} evt An object containing information about the fired event.
- * @param {Object} data Additional information about the change.
- * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi Conversion interface.
- */
-export function modelViewTextInsertion( view ) {
-	return ( evt, data, conversionApi ) => {
-		const parent = data.range.start.parent;
-
-		if ( parent.name != 'listItem' || parent.getAttribute( 'listType' ) != 'todo' ) {
-			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 );
-
-		// 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 );
-
-			return;
-		}
-
-		viewWriter.insert( textInsertionPosition, viewText );
-	};
-}
-
-/**
  * A model-to-view converter for the `listItem` model element insertion.
  *
  * It is used by {@link module:engine/controller/datacontroller~DataController}.
@@ -335,14 +288,12 @@ function createCheckmarkElement( modelItem, viewWriter, isChecked, onChange ) {
 }
 
 // Helper method to find label element inside li.
-function findLabel( viewItem, view ) {
+export function findLabel( viewItem, view ) {
 	const range = view.createRangeIn( viewItem );
 
-	let label;
 	for ( const value of range ) {
 		if ( value.item.is( 'uiElement', 'label' ) ) {
-			label = value.item;
+			return value.item;
 		}
 	}
-	return label;
 }

+ 24 - 3
packages/ckeditor5-list/src/todolistediting.js

@@ -15,12 +15,12 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 
 import {
 	modelViewInsertion,
-	modelViewTextInsertion,
 	dataModelViewInsertion,
 	dataModelViewTextInsertion,
 	dataViewModelCheckmarkInsertion,
 	modelViewChangeChecked,
-	modelViewChangeType
+	modelViewChangeType,
+	findLabel
 } from './todolistconverters';
 
 import { findInRange } from './utils';
@@ -73,7 +73,6 @@ export default class TodoListEditing extends Plugin {
 			modelViewInsertion( model, listItem => this._handleCheckmarkChange( listItem ) ),
 			{ 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:$text', dataModelViewTextInsertion, { priority: 'high' } );
 
@@ -86,6 +85,28 @@ export default class TodoListEditing extends Plugin {
 			modelViewChangeChecked( listItem => this._handleCheckmarkChange( listItem ) )
 		);
 
+		// Fix view position on 0 offset.
+		editing.mapper.on( 'modelToViewPosition', ( evt, data ) => {
+			const view = editing.view;
+			const modelPosition = data.modelPosition;
+
+			if ( modelPosition.parent.is( 'listItem' ) && modelPosition.parent.getAttribute( 'listType' ) == 'todo' ) {
+				const viewLi = editing.mapper.toViewElement( data.modelPosition.parent );
+
+				if ( modelPosition.offset === 0 ) {
+					const label = findLabel( viewLi, view );
+
+					if ( label ) {
+						if ( label.nextSibling ) {
+							data.viewPosition = view.createPositionAt( label.nextSibling, 0 );
+						} else {
+							data.viewPosition = view.createPositionAfter( label );
+						}
+					}
+				}
+			}
+		}, { priority: 'low' } );
+
 		data.upcastDispatcher.on( 'element:input', dataViewModelCheckmarkInsertion, { priority: 'high' } );
 
 		// Collect all view nodes that have changed and use it to check if the checkbox UI element is going to

+ 4 - 9
packages/ckeditor5-list/tests/todolistediting.js

@@ -424,9 +424,8 @@ describe( 'TodoListEditing', () => {
 			expect( getViewData( view ) ).to.equal(
 				'<ul class="todo-list">' +
 					'<li>' +
-						'<strong>' +
-							'<label class="todo-list__checkmark" contenteditable="false"></label>b{}foo' +
-						'</strong>' +
+				'<label class="todo-list__checkmark" contenteditable="false"></label>' +
+				'<strong>b{}foo</strong>' +
 					'</li>' +
 				'</ul>'
 			);
@@ -446,11 +445,8 @@ describe( 'TodoListEditing', () => {
 			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>' +
+				'<label class="todo-list__checkmark" contenteditable="false"></label>' +
+				'<a href="foo"><strong>b{}foo</strong></a>' +
 					'</li>' +
 				'</ul>'
 			);
@@ -822,7 +818,6 @@ describe( 'TodoListEditing', () => {
 				model: 'highlight',
 				view: { classes: 'highlight' }
 			} );
-
 			model.change( writer => {
 				writer.addMarker( 'element1', {
 					range: writer.createRangeIn( writer.createPositionAt( modelRoot.getChild( 0 ), 0 ) ),