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.36k stars 4.05k forks source link

[Bug] Changing layerable in an an event at runtime doesn't immediately update #2433

Closed Davidvlv closed 4 years ago

Davidvlv commented 4 years ago

When I change the layerable option of a custom component in an event, it still shows up in the layers until you drag it once. Is there some way to get around this by forcing the layers to update? Or can we get a bug fix for this, thanks.

import grapesjs from 'grapesjs';
import 'grapesjs-blocks-basic';

const editor = grapesjs.init({
  container: '#gjs',
  fromElement: 1,
  storageManager: { type: 0 },
  plugins: ['gjs-blocks-basic']
});

var comps = editor.DomComponents;

var defaultType = comps.getType('default');
var defaultModel = defaultType.model;

comps.addType('test', {
  model: defaultModel.extend({
    init: function() {
      this.on("change:attributes:id", () => {
        let attributes = this.getAttributes();
        this.set({'layerable': attributes.id === 'layerable'});
      });
    }
  })
});

editor.BlockManager.add('test', {
  label: 'test',
  category: 'something',
  content: '<div data-gjs-type="test" style="height: 20px; background-color: blue;" id="layerable"></div>'
});

Video: Grapesjs layerable.zip

Davidvlv commented 4 years ago

The same thing is happening when I try to add/remove a toolbar button in an event - the toolbar doesn't update until I unselect the component and reselect it:

      this.on("change:attributes:id", () => {
        let tb = model.get('toolbar');
        if (attributes.id === 'layerable') {
            if (tb.filter(b => b.attributes.class === 'fa fa-plus').length > 0)
                tb.pop();
        }
        else {
            if (tb.filter(b => b.attributes.class === 'fa fa-plus').length === 0)
                tb.push({
                    attributes: { class: 'fa fa-plus' },
                    command: () => {
                        model.components().add('<div>test</div');
                    }
                });
            }
      });
Davidvlv commented 4 years ago

Also, if you set 'removable' to false in the same place as where 'layerable' is being set in my post, the toolbar doesn't update to remove the delete button until you save and reload.

artf commented 4 years ago

Try to trigger this event after your updates editor.trigger('component:toggled')

Davidvlv commented 4 years ago

That seems to solve the second issue - the extra toolbar button now appears and disappears immediately.

However the change to 'layerable' still doesn't update the layers manager, and the 'clone', 'delete' and 'move' toolbar buttons don't disappear when 'copyable', 'removable' and 'draggable' are changed.

This is my code: https://codesandbox.io/s/youthful-hill-s1t0u

import grapesjs from "grapesjs";
import "grapesjs-blocks-basic";

const editor = grapesjs.init({
  container: "#gjs",
  fromElement: 1,
  storageManager: { type: 0 },
  plugins: ["gjs-blocks-basic"]
});

var comps = editor.DomComponents;

var defaultType = comps.getType("default");
var defaultModel = defaultType.model;

comps.addType("test", {
  model: defaultModel.extend({
    init: function() {
      this.on("change:attributes:id", () => {
        let attributes = this.getAttributes();
        // this doesn't work
        this.set({ layerable: attributes.id === "able" });
        this.set({ removable: attributes.id === "able" });

        // this works
        let tb = this.get("toolbar");
        if (attributes.id === "able") {
          if (tb.filter(b => b.attributes.class === "fa fa-plus").length === 0)
            tb.push({
              attributes: { class: "fa fa-plus" },
              command: () => {
                this.components().add("<div>test</div");
              }
            });
        } else {
          if (tb.filter(b => b.attributes.class === "fa fa-plus").length > 0)
            tb.pop();
        }
        editor.trigger("component:toggled");
      });
    }
  })
});

editor.BlockManager.add("test", {
  label: "test",
  category: "something",
  content:
    '<div data-gjs-type="test" style="height: 20px; background-color: blue;" id="able"></div>'
});
artf commented 4 years ago

However the change to 'layerable' still doesn't update the layers manager, and the 'clone', 'delete' and 'move' toolbar buttons don't disappear when 'copyable', 'removable' and 'draggable' are changed.

For the layerable I probably need to add a new listener in LayerManager... And about the toolbar buttons, those can't just disappear, you have to update the array toolbar property in the Component. You might try to re-init them with component.initToolbar()

sandenson commented 2 years ago

Try to trigger this event after your updates editor.trigger('component:toggled')

@artf this didn't work for me. I'm creating a component which can only be placed once in the canvas. Here's my code and screenshots:

editor.on('component:add', component => {
   const type = component!.get<string>('type')
   if (type === 'panda-video' && editor.getWrapper().findType(type).length > 1) {
     component!.remove();
     editor.trigger('component:toggled')
   }
});

The duplicate component is removed, but it still shows up in the Layer Manager.

image

Edit: I mate of mine suggested putting a slight timeout in the component!.remove() method call. It worked, but that's pretty ugly.

setTimeout(() => component!.remove(), 0.000001);