/** * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ /** * @module list/todolistconverters */ /* global document */ import { generateLiInUl, injectViewList, positionAfterUiElements, findNestedList } from './utils'; import createElement from '@ckeditor/ckeditor5-utils/src/dom/createelement'; /** * A model-to-view converter for the `listItem` model element insertion. * * It converts the `listItem` model element to an unordered list with a {@link module:engine/view/uielement~UIElement checkbox element} * at the beginning of each list item. It also merges the list with surrounding lists (if available). * * It is used by {@link module:engine/controller/editingcontroller~EditingController}. * * @see module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:insert * @param {module:engine/model/model~Model} model Model instance. * @param {Function} onCheckboxChecked Callback function. * @returns {Function} Returns a conversion callback. */ export function modelViewInsertion( model, onCheckboxChecked ) { return ( evt, data, conversionApi ) => { const consumable = conversionApi.consumable; if ( !consumable.test( data.item, 'insert' ) || !consumable.test( data.item, 'attribute:listType' ) || !consumable.test( data.item, 'attribute:listIndent' ) ) { return; } if ( data.item.getAttribute( 'listType' ) != 'todo' ) { return; } const modelItem = data.item; consumable.consume( modelItem, 'insert' ); consumable.consume( modelItem, 'attribute:listType' ); consumable.consume( modelItem, 'attribute:listIndent' ); consumable.consume( modelItem, 'attribute:todoListChecked' ); const viewWriter = conversionApi.writer; const viewItem = generateLiInUl( modelItem, conversionApi ); const isChecked = !!modelItem.getAttribute( 'todoListChecked' ); const checkmarkElement = createCheckmarkElement( modelItem, viewWriter, isChecked, onCheckboxChecked ); const span = viewWriter.createContainerElement( 'span', { class: 'todo-list__label__description' } ); viewWriter.addClass( 'todo-list', viewItem.parent ); viewWriter.insert( viewWriter.createPositionAt( viewItem, 0 ), checkmarkElement ); viewWriter.insert( viewWriter.createPositionAfter( checkmarkElement ), span ); injectViewList( modelItem, viewItem, conversionApi, model ); }; } /** * A model-to-view converter for the `listItem` model element insertion. * * It is used by {@link module:engine/controller/datacontroller~DataController}. * * @see module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:insert * @param {module:engine/model/model~Model} model Model instance. * @returns {Function} Returns a conversion callback. */ export function dataModelViewInsertion( model ) { return ( evt, data, conversionApi ) => { const consumable = conversionApi.consumable; if ( !consumable.test( data.item, 'insert' ) || !consumable.test( data.item, 'attribute:listType' ) || !consumable.test( data.item, 'attribute:listIndent' ) ) { return; } if ( data.item.getAttribute( 'listType' ) != 'todo' ) { return; } const modelItem = data.item; consumable.consume( modelItem, 'insert' ); consumable.consume( modelItem, 'attribute:listType' ); consumable.consume( modelItem, 'attribute:listIndent' ); consumable.consume( modelItem, 'attribute:todoListChecked' ); const viewWriter = conversionApi.writer; const viewItem = generateLiInUl( modelItem, conversionApi ); viewWriter.addClass( 'todo-list', viewItem.parent ); const label = viewWriter.createContainerElement( 'label', { class: 'todo-list__label' } ); const checkbox = viewWriter.createEmptyElement( 'input', { type: 'checkbox', disabled: 'disabled' } ); const span = viewWriter.createContainerElement( 'span', { class: 'todo-list__label__description' } ); if ( modelItem.getAttribute( 'todoListChecked' ) ) { viewWriter.setAttribute( 'checked', 'checked', checkbox ); } viewWriter.insert( viewWriter.createPositionAt( viewItem, 0 ), label ); viewWriter.insert( viewWriter.createPositionAt( label, 0 ), checkbox ); viewWriter.insert( viewWriter.createPositionAfter( checkbox ), span ); injectViewList( modelItem, viewItem, conversionApi, model ); }; } /** * A view-to-model converter for the checkbox element inside a view list item. * * It changes the `listType` of the model `listItem` to a `todo` value. * When a view checkbox element is marked as checked, an additional `todoListChecked="true"` attribute is added to the model item. * * It is used by {@link module:engine/controller/datacontroller~DataController}. * * @see module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:element * @param {module:utils/eventinfo~EventInfo} evt An object containing information about the fired event. * @param {Object} data An object containing conversion input, a placeholder for conversion output and possibly other values. * @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi Conversion interface to be used by the callback. */ export function dataViewModelCheckmarkInsertion( evt, data, conversionApi ) { const modelCursor = data.modelCursor; const modelItem = modelCursor.parent; const viewItem = data.viewItem; if ( viewItem.getAttribute( 'type' ) != 'checkbox' || modelItem.name != 'listItem' || !modelCursor.isAtStart ) { return; } if ( !conversionApi.consumable.consume( viewItem, { name: true } ) ) { return; } const writer = conversionApi.writer; writer.setAttribute( 'listType', 'todo', modelItem ); if ( data.viewItem.hasAttribute( 'checked' ) ) { writer.setAttribute( 'todoListChecked', true, modelItem ); } data.modelRange = writer.createRange( modelCursor ); } /** * A model-to-view converter for the `listType` attribute change on the `listItem` model element. * * This change means that the `
  • ` element parent changes to `