javascriptreactjsckeditorckeditor5ckeditor4.x

CK Editor Inline element drag and drop issue


I have created a custom plugin as show in the image. I can add the object with class "placeholder". <span class="placeholder">Loop-ANS(Q2)</span> .

enter image description here

inline widget created from Created from

inline widget Demo Demo

Now when someone remove this from the editor i need to generate an event (call a function) so that i can mark this item has removed in my redux state. I got a solution for this with the code below.

editor.model.document.on( 'change:data', () => {  // Listen to all changes in model.
    // Get changes buffered in Differ. Include items moved to the graveyard (removed from document).
    const changes = editor.model.document.differ.getChanges( { includeChangesInGraveyard: true } );

    for ( const change of changes ) {
        // Check items moved to the graveyard (so they are removed from the document root).
        if ( change.type == 'insert' && change.name != '$text' && change.position.root.rootName == '$graveyard' ) {

            // The element that we are looking for could be nested in some block-quote or a table cell
            // so we need to walk through the whole element content and check if our element was removed or not.
            const range = editor.model.createRangeOn( change.position.nodeAfter );

            for ( const item of range.getItems() ) {
                if ( item.is( 'element', 'imageInline' ) ) { // Here you change the value to your element's name.
                    console.log( 'inline image removed', item ); // (**I will add a callback here**)
                }
            }
        }
    }
} );

The issue is, when i try to drag and drop the inline-widget, that time also it is considered as removed. ie, the element is moving to Graveyard, which shouldn't. Is there any way to prevent this ? the placeholder not to move on gravyard while drag and drop to other position. Or to check the event is generated due to drag and drop and not call the callback.

If it is not possible, any way to stop the drag n drop event of inline widget


Solution

  • If anyone need this functionality later, you can refer to this code:

    function _getPlaceholderChanges(editor, inGraveyard) {
      const changes = editor.model.document.differ.getChanges({
        includeChangesInGraveyard: inGraveyard,
      });
      let changedItems = [];
      for (const change of changes) {
        if (
          inGraveyard
            ? change.type == "insert" &&
              change.name != "$text" &&
              change.position.root.rootName == "$graveyard"
            : change.type == "insert" && change.name != "$text"
        ) {
          const range = editor.model.createRangeOn(change.position.nodeAfter);
          for (const item of range.getItems()) {
            if (item.is("element", "placeholder")) {
              changedItems.push(item);
            }
          }
        }
      }
      const changedPlaceholders = changedItems.map((item) =>
        item._attrs.get("name")
      );
      return changedPlaceholders;
    }
    
    function placeholderDeletionChecker(editor, questionId, callback) {
      const model = editor.model;
      model.document.on("change:data", () => {
        // Get changes buffered in Differ. Include items moved to the graveyard (removed from document).
        const removedPlaceHolders = _getPlaceholderChanges(editor, true);
        const addedPlaceHolders = _getPlaceholderChanges(editor, false);
    
        //if a placeholder deleted and added on same time means, this is happend due to reposition of item (drag and drop).
        //In this case we wont call the callback
        if (removedPlaceHolders.length && !addedPlaceHolders.length) {
          callback(questionId, removedPlaceHolders);
        }
      });
    }
    
    export default placeholderDeletionChecker;