Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Handle todo list checkbox by fixing modelToViewPosition mapping.
Browse files Browse the repository at this point in the history
  • Loading branch information
jodator committed Sep 16, 2019
1 parent f84a6a1 commit cc87dba
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 63 deletions.
53 changes: 2 additions & 51 deletions src/todolistconverters.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,53 +59,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.
*
Expand Down Expand Up @@ -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;
}
27 changes: 24 additions & 3 deletions src/todolistediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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' } );

Expand All @@ -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
Expand Down
13 changes: 4 additions & 9 deletions tests/todolistediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -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>'
);
Expand All @@ -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>'
);
Expand Down Expand Up @@ -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 ) ),
Expand Down

0 comments on commit cc87dba

Please sign in to comment.