ckeditor / ckeditor5

Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.
https://ckeditor.com/ckeditor-5
Other
9.5k stars 3.7k forks source link

Help me getting started. #14974

Open devraj112 opened 1 year ago

devraj112 commented 1 year ago

What are the pre-requisites before delving deeper into the ckeditor5. I have already went through the documentation and I am still not able to create the required schema and conversion. I have practiced the basic plugin creation, advance plugin and BlockWidget plugin also. Now I want to create some custom plugin. Any help would be appreciated. Thanks in advance.

filipsobol commented 1 year ago

Hi @devraj112,

A good starting point is our new Crash Course, which has pages dedicated to Model and schema and Data conversion. This tutorial only scratches the surface, so if you want to dig deeper, you can check out the Framework section, for example the Editing engine and Conversion pages.

If you prefer to look at the working examples, I recommend checking out one of our plugins. Find the one that works similar to what you want to implement and use it as a reference.

devraj112 commented 1 year ago

Thank you for responding, I have went through the crash course and it is very well written also. For the reference I am referring to the basic styles plugin for creating my Index plugin. In my plugin I am able to pass one attribute but not more than one.

Check the schema.

`const schema = this.editor.model.schema

        schema.extend( '$text', {
            allowAttributes: ['INDEX']
        })
        schema.setAttributeProperties( 'INDEX', {
            isFormatting: true,
            copyOnEnter: true
        })

Check the conversion

const conversion = this.editor.conversion
` conversion.for('upcast').elementToAttribute({
            view: {
                name: 'index',
                attributes: {
                    class: true,
                    id: true
                }
            },
            model: 
            {
                key: 'INDEX',
                value: (viewElement: Element) => {
                    const id = viewElement.getAttribute('id')
                    return [
                        id, 
                    ]
                }
            },
            converterPriority: 'high'
        })

        conversion.for('downcast').attributeToElement({
            model: 'INDEX',
            view : (modelElementValue, conversionApi) => {
                const writer  = conversionApi.writer
                // console.log('line 47', modelElementValue, writer)

                return writer.createAttributeElement( 'index', 
                // { class: 'ck-vertical'},
                { id: Math.floor(Math.random()*10000)+modelElementValue},
                )
            },
        })`

What changes do you think I should make to be able to pass more than one attribute?

filipsobol commented 1 year ago

What do you mean by "pass one attribute"? Can you share an example so I can understand this better?

So far, if I understand correctly, you have a custom component named 'index' which has 'id' and class attributes that you want to represent in the editor's model as a text attribute?

editor.setData(`<p>Hello <index id="2508" class="test-class">world</index>!</p>`)

In upcast, you return an array which you can then read in downcast.

editor.conversion.for( 'upcast' ).elementToAttribute( {
  view: {
    name: 'index',
      attributes: {
        id: /[0-9]+/ // Ensures that `id` is a number
      },
      classes: true // Moved from `attributes`, as this approach was deprecated
    },
    model: {
      key: 'INDEX',
      value: ( viewElement ) => [
        // Return `id` and `class`
        Number( viewElement.getAttribute('id') ), // Cast `id` to number
        viewElement.getAttribute('class')
      ]
    },
    converterPriority: 'high'
} );

editor.conversion.for( 'downcast' ).attributeToElement( {
  model: 'INDEX',
  view : ( modelElementValue, conversionApi ) => {
    if ( !modelElementValue ) {
      return;
    }

    console.log( modelElementValue ); // [ 2508, "test-class" ]
  },
} );
devraj112 commented 1 year ago

Thank you for responding, Yes, you are on the point with the representation. editor.setData(<p>Hello <index id="2508" class="test-class">world</index>!</p>)

But, my I have done some alteration with my structure and I want to create something which is more specific to my requirement. Which is: editor.setData(<p>Hello <index id="3452">world<sup>P</sup></index>!</p>)

For this, I am following the simpleBox for creating the schema and conversion.

   `const schema = this.editor.model.schema

    schema.register('INDEX', {
        allowWhere: '$text',
        allowAttributes: ['id'],
        isInline: true,
        isLimit: true
    })

    schema.register('super', {
        allowIn: ['INDEX'],
        isLimit: true,
    })

    const conversion = this.editor.conversion;  

    conversion.for('upcast').elementToElement({
        view: {
            name: 'index',
            attributes: ['id'],
        },
        model: 'INDEX'
    })

    conversion.for('dataDowncast').elementToElement({
        model: {
            name: 'INDEX',
            attributes: ['id']
        },
        view: ( modelElement, { writer } ) => {
            return writer.createContainerElement(
                'index' + modelElement.getAttribute( 'id' )
            )}
    })

    conversion.for('editingDowncast').elementToElement({
        model: 'INDEX',
        view: ( modelElement, { writer } ) => {
            return writer.createContainerElement( 'index', { id: Math.floor(Math.random()*10000)+modelElement} );
        }
    })

    conversion.for('upcast').elementToAttribute({
        view: 'sup',
        model: 'super'
    })

    conversion.for('dataDowncast').elementToElement({
        model: 'super',
        view: 'sup'
    })

    conversion.for('editingDowncast').elementToElement({
        model: 'super',
        view: (modelElement, {writer}) =>{
            return writer.createContainerElement('sup')
        }
    })`

Do you think this would work?

CKEditorBot commented 2 weeks ago

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 still be relevant, so if you're interested in the solution, leave a comment or reaction under this issue.