GrapesJS / grapesjs

Free and Open source Web Builder Framework. Next generation tool for building templates without coding
https://grapesjs.com
BSD 3-Clause "New" or "Revised" License
22.38k stars 4.06k forks source link

[QUESTION] Custom type unrecognized on import #2862

Closed Lerdouille closed 4 years ago

Lerdouille commented 4 years ago

Hi,

i succeed to create a new type containing a clickable image. But unfortunaltely, the new custom type isn't recognize by the HTML parser on import.

Here is my new type source code :

`editor.DomComponents.addType('imagelink', {

            isComponent: el => {
                if (el.tagName === 'DIV' && (typeof el.classList !== 'undefined' && el.classList.contains('imagelink-class'))) {
                  return { type: 'imagelink' };
                }
            },
            model: {
                defaults: { 
                   tagName: 'div',
                    resizable: {keepAutoHeight: true},
                    attributes: {class: 'imagelink-class', "data-type": "imagelink"},
                    droppable: false,
                    traits: [
                        { 
                            type: 'href', label: 'Source', name: 'href'
                        },
                        {
                            type: 'select',
                            label: 'Target',
                            name: 'target',
                            options: [
                              { value: '', name: 'This window' },
                              { value: '_blank', name: 'New window' }
                            ]
                        }
                    ],
                    components: [
                        {
                            resizable: false,
                            draggable: false,
                            droppable: false,
                            selectable: false,
                            hoverable: false,
                            type: 'link',
                            components: [{
                                type: 'image',
                                attributes: {
                                    src: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIHZpZXdCb3g9IjAgMCAyNCAyNCIgc3R5bGU9ImZpbGw6IHJnYmEoMCwwLDAsMC4xNSk7IHRyYW5zZm9ybTogc2NhbGUoMC43NSkiPgogICAgICAgIDxwYXRoIGQ9Ik04LjUgMTMuNWwyLjUgMyAzLjUtNC41IDQuNSA2SDVtMTYgMVY1YTIgMiAwIDAgMC0yLTJINWMtMS4xIDAtMiAuOS0yIDJ2MTRjMCAxLjEuOSAyIDIgMmgxNGMxLjEgMCAyLS45IDItMnoiPjwvcGF0aD4KICAgICAgPC9zdmc+',
                                    style:'width:100%;'
                                },
                                resizable: false,
                                draggable: false,
                                droppable: false,
                                hoverable: false,
                                selectable: false,
                            }]
                        }
                    ]
                },
                init() {
                  this.on('change:attributes:href', this.handleHrefChange);
                  this.on('change:attributes:target', this.handleTargetChange);
                },
                handleHrefChange: function ( component, value ) {
                    delete component.attributes.attributes.href;
                    delete component.attributes.attributes.target;
                    component.attributes.components.models[0].attributes.attributes.href = value;
                    return false;
                },
                handleTargetChange: function ( component, value ) {
                    delete component.attributes.attributes.href;
                    delete component.attributes.attributes.target;
                    component.attributes.components.models[0].attributes.attributes.target = value;
                    return false;
                },
            },
        });`

I precise the definition of the new type is include in a plugin, which is passed in the editor initialization :

var editor = grapesjs.init({ .... plugins: [myplugin]; ... }

I tried to use the isComponent parameter to do so, the condition is good, but the plugin render all the dom components separately (<img> and <a>) instead of the "imagelink" type. So when i click on the image, i can't change the traits defined in my type.

I think i'm missing some points, but i can't figure it out... Could you please tell me what's wrong ?

I know it is possible, for example the plugin 'gjs-social' and its type 'Social-link-block' works perfecty...

Best regards

Lerdouille commented 4 years ago

Hi,

for the record, when i try to change the isComponent return, with a type defined in a plugin like this :

isComponent: el => { if (el.tagName === 'DIV' && (typeof el.classList !== 'undefined' && el.classList.contains('imagelink-class'))) { return { type: 'marketing-button' }; } },

the import will recognize my <div><a><img></a></div> DOM results as a marketing-button (defined in the plugin grapesjs-block-avance

I think i miss something in the type definition to help grapesjs to recognize my type on import....

Best regards

Lerdouille commented 4 years ago

Another curiosity which might be logical... If i tried to put a exotic isComponent function like this :

if (el.tagName === 'ABCDE') { return { type: 'imagelink' }; }

And when i try to import this html text : <abcde>test</abcde> The plugin detect correctly my "imagelink" custom type.

BUT, when i import this html text : <abcde><img src="ggggg"></abcde>

it didn't work. I guess the plugin stil go further in the component recognition and detect the IMG and A dom, and override so the detection...

artf commented 4 years ago

I think you're missing the point of components. You have a custom one (imagelink) and then an inner one (img), so the editor shows it, so I see no issues here. To see the traits from the custom component you have to select it and it has nothing to do with the inner image

Lerdouille commented 4 years ago

Well, actually if you wan to add a type composed with a complex DOM tree (, ,

...) we may want to set up the new component globally and not configure each elements separately. The image redirection on click is a perfect example (you can of course add a link component and an image inside, but it's quite difficult to understand if you're not familiar with HTML language.

Anyway, i succeed to solve my issue by adding some attributes to each component like this :

components: [ { resizable: false, draggable: false, droppable: false, selectable: false, hoverable: false, propagate: ['hoverable', 'draggable', 'resizable', 'droppable', 'selectable'], type: 'link', attributes: { "data-gjs-resizable": "false", "data-gjs-draggable": "false", "data-gjs-droppable": "false", "data-gjs-selectable": "false", "data-gjs-hoverable": "false" }, components: [{ type: 'image', attributes: { style:'width:100%;', "data-gjs-resizable": "false", "data-gjs-draggable": "false", "data-gjs-droppable": "false", "data-gjs-selectable": "false", "data-gjs-hoverable": "false" } }] }

By doing this, when i save my canvas and import the source code, the editor will see the data atttributes selectable. So when i click on the component, the editor will select the highest level component, my imagelink new type.

Regards

artf commented 4 years ago

I hope you don't use HTML to load back your stored templates...

Lerdouille commented 4 years ago

Yes i do, why ?

The component is well recognize but i have another issue :'( Actually not all styles are imported correctly.

For example, if i change the text color, background color and width parameters, only the background color is correctly applied and imported when i import the stored template.

Regards

artf commented 4 years ago