Is CKEditor 5 widget with only 1 container possible?

13 Views Asked by At

I'm trying to understand if I can make this into CKEditor 5 widget so that it would retain the widgets "add empty line before or after the widget" buttons and the yellow border:

<div class="widget-tip">
  <p>
    Tip:<br>
    lorem ipsum
  </p>
</div>

Only way I could make this work was if applying toWidget() to the widget-tip div, then also creating another div inside it and applying toWidgetEditable() to that.

I'm trying to widgetize my existing templates, because there's no more https://ckeditor.com/cke4/addon/magicline in CKEditor 5 and it's a bit hard to add new lines after divs - after adding new template, you'd have to go to source, add some text after the new block, then come out of the source mode and continue edititing.

Here's picture of the widget that has the nice yellow border and the next/prev empty line buttons. widget example

I could go with the new stucture, but our system already has the old templates in the db. I'd either have to alter them all in db or create something in the widget to add the missing inner container (I did try that, but failed).

Here's my current work. Does anybody have any ideas?

import { Plugin } from 'ckeditor5/src/core';
import { Widget, toWidget, toWidgetEditable } from 'ckeditor5/src/widget';

export default class WidgetTip extends Plugin {
  static get requires() {
    return [Widget];
  }

  init() {
    this._defineSchema();
    this._defineConverters();
  }

  _defineSchema() {
    const schema = this.editor.model.schema;

    schema.register('widgetTip', {
      // Behaves like a self-contained block object (e.g. a block image)
      // allowed in places where other blocks are allowed (e.g. directly in the root).
      inheritAllFrom: '$blockObject'
    });

    schema.register('widgetTipInner', {
      // Cannot be split or left by the caret.
      isLimit: true,

      allowIn: 'widgetTip',

      // Allow content which is allowed in the root (e.g. paragraphs).
      allowContentOf: '$root'
    });

  }

  _defineConverters() {
    const conversion = this.editor.conversion;

    // <widgetTip> converters
    conversion.for('upcast').elementToElement({
      model: 'widgetTip',
      view: {
        name: 'div',
        classes: 'widget-tip'
      }
    });
    conversion.for('dataDowncast').elementToElement({
      model: 'widgetTip',
      view: {
        name: 'div',
        classes: 'widget-tip'
      }
    });
    conversion.for('editingDowncast').elementToElement({
      model: 'widgetTip',
      view: (modelElement, { writer: viewWriter }) => {
        const div = viewWriter.createContainerElement('div', {
          class: 'widget-tip'
        });

        return toWidget(div, viewWriter, { label: 'simple widgetTip widget' });
      }
    });

    // <widgetTipInner> converters
    conversion.for('upcast').elementToElement({
      model: 'widgetTipInner',
      view: {
        name: 'div',
        classes: 'widget-tip__inner'
      }
    });
    conversion.for('dataDowncast').elementToElement({
      model: 'widgetTipInner',
      view: {
        name: 'div',
        classes: 'widget-tip__inner'
      }
    });
    conversion.for('editingDowncast').elementToElement({
      model: 'widgetTipInner',
      view: (modelElement, { writer: viewWriter }) => {
        // Note: You use a more specialized createEditableElement() method here.
        const div = viewWriter.createEditableElement('div', {
          class: 'widget-tip__inner'
        });

        return toWidgetEditable(div, viewWriter);
      }
    });
  }
}

0

There are 0 best solutions below