Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example Complex Converter for Paragraph Model #11044

Closed
58bits opened this issue Dec 23, 2021 · 3 comments
Closed

Example Complex Converter for Paragraph Model #11044

58bits opened this issue Dec 23, 2021 · 3 comments
Labels
resolution:expired This issue was closed due to lack of feedback. status:stale type:task This issue reports a chore (non-production change) and other types of "todos".

Comments

@58bits
Copy link

58bits commented Dec 23, 2021

I realize this is a round-about-way of making a support request, so feel free to close this if this in deemed inappropriate.

I've posted the following question on Stackoverflow...

https://stackoverflow.com/questions/70464474/ckeditor-5-convert-paragraph-model-to-div-with-inner-span

...and would love to find an example or some guidance on how to create an elementToElement converter that would allow us to convert paragraph models into <div><span>Text here...</span></div> or even <p><span>Text here...</span></p> structure (as opposed to only <p>text</p> elements).

I'd be glad to submit a pull request, or help update docs if I could be pointed in the right direction.

I've read the block, inline-block tutorials, and the Custom Element conversion example here.. https://ckeditor.com/docs/ckeditor5/latest/framework/guides/deep-dive/conversion/custom-element-conversion.html - but I'm still struggling to put this together.

I can create the following - as per my update above in SO:

<div>
   Text Here...
   <span></span>
</div>

And I understand that the model position needs to be updated to reflect the view structure, so that the model data appears inside the span, and not before it. But I'm stuck.

Any pointers, or suggestions greatly appreciated.

@58bits 58bits added the type:task This issue reports a chore (non-production change) and other types of "todos". label Dec 23, 2021
@58bits
Copy link
Author

58bits commented Jan 28, 2022

For anyone else interested in this, I've posted a working example here https://stackoverflow.com/questions/70883867/ckeditor-5-downcast-converter-for-paragraph-to-wrap-text-in-span/70888177#70888177, as well as the code below...

Based loosely on this example here... https://ckeditor.com/docs/ckeditor5/latest/framework/guides/deep-dive/conversion/custom-element-conversion.html

... here's what I've come up with...

/**
 * Helper method to map model to view position
 * 
 * @param {*} view 
 */
function createModelToViewPositionMapper(view) {
  return (evt, data) => {
    const modelPosition = data.modelPosition;
    const parent = modelPosition.parent;

    // Only the mapping of positions that are directly in
    // the <paragraph> model element should be modified.
    if (!parent || !parent.is('element', 'paragraph')) {
      return;
    }

    // Get the mapped view element <div class="data-block">.
    const viewElement = data.mapper.toViewElement(parent);

    // Find the <div class="data-text"> in it.
    const viewContentElement = findContentViewElement( view, viewElement );

    // Translate the model position offset to the view position offset.
    data.viewPosition = data.mapper.findPositionIn( viewContentElement, modelPosition.offset );
  };
}

/**
 * Helper method to find child span
 * 
 * @param {*} editingView 
 * @param {*} viewElement 
 * @returns <span class="data-text"> nested in the info box view structure. 
 */
function findContentViewElement( editingView, viewElement ) {
  for ( const value of editingView.createRangeIn( viewElement ) ) {
      if ( value.item.is( 'element', 'span' ) && value.item.hasClass( 'data-text' ) ) {
          return value.item;
      }
  }
}

/**
 * Paragraph model downcast converter to wrap all text nodes in 
 * inline span elements
 * 
 * @param {*} editor 
 */
function ParagraphConverter(editor) {
  editor.conversion.for('downcast').add(dispatcher => {
    dispatcher.on('insert:paragraph', (evt, data, conversionApi) => {
      // Remember to check whether the change has not been consumed yet and consume it.
      if (!conversionApi.consumable.consume(data.item, 'insert')) {
        return;
      }
      const { writer, mapper } = conversionApi

      // Translate the position in the model to a position in the view.
      const viewPosition = mapper.toViewPosition(data.range.start);

      // Create a <div> element that will be inserted into the view at the `viewPosition`.
      const div = writer.createContainerElement('div', { class: 'data-block' });
      
      // Create the <span> element that will be inserted into the div
      const span = writer.createEditableElement('span', { class: 'data-text' });
      writer.insert(writer.createPositionAt(div, 0), span);

      // Bind the newly created view element to the model element so positions will map accordingly in the future.
      mapper.bindElements(data.item, div);

      // Add the newly created view element to the view.
      writer.insert(viewPosition, div);

      // Remember to stop the event propagation.
      evt.stop();
    });
  });

  // Dynamic mapping for model to view and curser position with correct offset
  editor.editing.mapper.on( 'modelToViewPosition', createModelToViewPositionMapper( editor.editing.view ) );
  editor.data.mapper.on( 'modelToViewPosition', createModelToViewPositionMapper( editor.editing.view ) );
}

@CKEditorBot
Copy link
Collaborator

There has been no activity on this issue for the past year. We've marked it as stale and will close it in 30 days. We understand it may be relevant, so if you're interested in the solution, leave a comment or reaction under this issue.

@CKEditorBot
Copy link
Collaborator

We've closed your issue due to inactivity over the last year. We understand that the issue may still be relevant. If so, feel free to open a new one (and link this issue to it).

@CKEditorBot CKEditorBot added the resolution:expired This issue was closed due to lack of feedback. label Oct 30, 2023
@CKEditorBot CKEditorBot closed this as not planned Won't fix, can't repro, duplicate, stale Oct 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
resolution:expired This issue was closed due to lack of feedback. status:stale type:task This issue reports a chore (non-production change) and other types of "todos".
Projects
None yet
Development

No branches or pull requests

2 participants