ckeditor_accordion-8.x-1.3/js/ckeditor5_plugins/accordion/src/accordionediting.js
js/ckeditor5_plugins/accordion/src/accordionediting.js
import { Plugin } from 'ckeditor5/src/core'; import { toWidget, toWidgetEditable } from 'ckeditor5/src/widget'; import { Widget } from 'ckeditor5/src/widget'; import InsertAccordionCommand from './insertaccordioncommand'; import InsertAccordionRowCommand from './insertaccordionrowcommand'; import DeleteAccordionRowCommand from './deleteaccordionrowcommand'; // cSpell:ignore accordion insertsimpleboxcommand /** * CKEditor 5 plugins do not work directly with the DOM. They are defined as * plugin-specific data models that are then converted to markup that * is inserted in the DOM. * * CKEditor 5 internally interacts with accordion as this model: * <accordion> * <accordionTitle></accordionTitle> * <accordionContent></accordionContent> * </accordion> * * Which is converted for the browser/user as this markup * <section class="simple-box"> * <h2 class="simple-box-title"></h1> * <div class="simple-box-description"></div> * </section> * * This file has the logic for defining the accordion model, and for how it is * converted to standard DOM markup. */ export default class AccordionEditing extends Plugin { /** * @inheritDoc */ static get pluginName() { return 'AccordionEditing'; } static get requires() { return [Widget]; } init() { this._defineSchema(); this._defineConverters(); this.editor.commands.add( 'insertAccordion', new InsertAccordionCommand(this.editor), ); this.editor.commands.add( 'insertAccordionRowAbove', new InsertAccordionRowCommand(this.editor, { order: 'above' }) ); this.editor.commands.add( 'insertAccordionRowBelow', new InsertAccordionRowCommand( this.editor, { order: 'below' } ) ); this.editor.commands.add( 'deleteAccordionRow', new DeleteAccordionRowCommand( this.editor, {} ) ); } /* * This registers the structure that will be seen by CKEditor 5 as * <accordion> * <accordionTitle></accordionTitle> * <accordionContent></accordionContent> * </accordion> * * The logic in _defineConverters() will determine how this is converted to * markup. */ _defineSchema() { // Schemas are registered via the central `editor` object. const schema = this.editor.model.schema; schema.register('accordion', { // Behaves like a self-contained object (e.g. an image). isObject: true, // Allow in places where other blocks are allowed (e.g. directly in the root). allowWhere: '$block', }); schema.register( 'accordionRow', { allowIn: 'accordion', isLimit: true } ); schema.register('accordionTitle', { // This creates a boundary for external actions such as clicking and // and keypress. For example, when the cursor is inside this box, the // keyboard shortcut for "select all" will be limited to the contents of // the box. isLimit: true, // This is only to be used within accordion. allowIn: 'accordion', // Allow content that is allowed in blocks (e.g. text with attributes). allowContentOf: '$block', }); schema.register('accordionContent', { isLimit: true, allowIn: 'accordion', allowContentOf: '$root', }); schema.addChildCheck((context, childDefinition) => { // Disallow accordion inside accordionContent. if ( context.endsWith('accordionContent') && childDefinition.name === 'accordion' ) { return false; } }); } /** * Converters determine how CKEditor 5 models are converted into markup and * vice-versa. */ _defineConverters() { // Converters are registered via the central editor object. const { conversion } = this.editor; // Upcast Converters: determine how existing HTML is interpreted by the // editor. These trigger when an editor instance loads. // // If <dl class="ckeditor-accordion"> is present in the existing markup // processed by CKEditor, then CKEditor recognizes and loads it as a // <accordion> model. conversion.for('upcast').elementToElement({ model: 'accordion', view: { name: 'dl', classes: 'ckeditor-accordion', }, }); // If <dt> is present in the existing markup // processed by CKEditor, then CKEditor recognizes and loads it as a // <accordionTitle> model, provided it is a child element of <accordion>, // as required by the schema. conversion.for('upcast').elementToElement({ model: 'accordionTitle', view: { name: 'dt', classes: '', }, }); // If <dd> is present in the existing markup // processed by CKEditor, then CKEditor recognizes and loads it as a // <accordionContent> model, provided it is a child element of // <accordion>, as required by the schema. conversion.for('upcast').elementToElement({ model: 'accordionContent', view: { name: 'dd', classes: '', }, }); // Data Downcast Converters: converts stored model data into HTML. // These trigger when content is saved. // // Instances of <accordion> are saved as // <dl class="ckeditor-accordion">{{inner content}}</dl>. conversion.for('dataDowncast').elementToElement({ model: 'accordion', view: { name: 'dl', classes: 'ckeditor-accordion', }, }); // Instances of <accordionTitle> are saved as // <dt>{{inner content}}</dt>. conversion.for('dataDowncast').elementToElement({ model: 'accordionTitle', view: { name: 'dt', classes: '', }, }); // Instances of <accordionContent> are saved as // <dd>{{inner content}}</dd>. conversion.for('dataDowncast').elementToElement({ model: 'accordionContent', view: { name: 'dd', classes: '', }, }); // Editing Downcast Converters. These render the content to the user for // editing, i.e. this determines what gets seen in the editor. These trigger // after the Data Upcast Converters, and are re-triggered any time there // are changes to any of the models' properties. // // Convert the <accordion> model into a container widget in the editor UI. conversion.for('editingDowncast').elementToElement({ model: 'accordion', view: (modelElement, { writer: viewWriter }) => { const div = viewWriter.createContainerElement('div', { class: 'ckeditor-accordion', }); return toWidget(div, viewWriter); }, }); // Convert the <accordionTitle> model into an editable <h2> widget. conversion.for('editingDowncast').elementToElement({ model: 'accordionTitle', view: (modelElement, { writer: viewWriter }) => { const h2 = viewWriter.createEditableElement('div', { class: 'ckeditor-accordion-title', }); return toWidgetEditable(h2, viewWriter); }, }); // Convert the <accordionContent> model into an editable <div> widget. conversion.for('editingDowncast').elementToElement({ model: 'accordionContent', view: (modelElement, { writer: viewWriter }) => { const div = viewWriter.createEditableElement('div', { class: 'ckeditor-accordion-content', }); return toWidgetEditable(div, viewWriter); }, }); } }