Skip to content

Conversation

@artf
Copy link
Member

@artf artf commented Dec 1, 2020

Hi guys,
in the past few days, I've started working on this WIP branch to introduce a new feature in GrapesJS: Symbols.
The concept of Symbols is already quite famous, like in visual design tools (eg. Sketch), and they should allow users to reuse common elements across pages.

GrapesJS allows already the reuse of styles (CSS) by using Classes, Symbols will introduce also the reuse of the structure (HTML). By editing children, contents, props of the main Symbol, will reflect these changes to all connected instances.

The approach that I'd like to keep trying, differently from other Symbol implementations I've seen around, is to maintain a one-way binding between the main Symbols and their instances. That means if you update the main Symbol, the changes will propagate to their corresponding instances and not vice-versa. This approach should guarantee instances to be overridable by design, which means the user will be able to change the Symbol's instances independently from the main one. This behavior can also be observed from this GIF (codepen demo)

symbols

The code

The code didn't change that much and all the changes can be easily merged without breaking the current behavior as the Symbol is created only on request (for now, from Component.clone method). I have to say that I didn't though yet about public APIs and quite sure won't introduce any UI changes, the feature should be activated via API, all you need is a simple command and decide how/where to place its activation (panels, toolbar, etc.). Here an example of a simple command to clone the component as a Symbol (+ button for the options panel)

// Command
editor.Commands.add('clone-symbol', (ed) => {
  const main = ed.getSelected();
  const child = main.clone({ symbol: 1 });
  const at = main.index() + 1;
  main.parent().append(child, { at });
});
// Button
editor.Panels.addButton('options', {
  id: 'clone-symb',
  className: 'fa fa-clone',
  command: 'clone-symbol',
  attributes: { title: 'Clone as Symbol' }
});

How Symbols are identified

The Component identified as Symbol will have a new array property __symbol containing the reference to all of its instances. All Symbol's instances will have instead the __symbolOf property, pointing to the main symbol component.
This approach allows also having nested Symbols.
Below the pseudo-code of the Component Definition:

ComponentX { // Main Symbol component
  ... other props
  __symbol: [ComponentA, ComponentB],
}

ComponentA { // Symbol instance of ComponentX
  ... other props
  __symbolOf: ComponentX,
}
ComponentB { // Symbol instance of ComponentX
  ... other props
  __symbolOf: ComponentX,
}

Conclusions

Obviously, this feature is still far from being complete and stable, I've noticed already a few weird behaviors in conjunction with the UndoManager but those problems can be fixed. The thing I have to see if the chosen approach works in a real-life scenario and makes sense for others, so if you have time, try to play with the demo and report any weird behaviors/unusual cases.

@micker
Copy link

micker commented Dec 1, 2020

oh very cool !

@BerkeAras
Copy link
Contributor

👍 A very nice feature, good work @artf 😄

@artf artf merged commit 78386c9 into dev Dec 6, 2020
@abozhinov
Copy link

@artf Nice feature! Good job. It will be nice if you have Symbols like Webflow. Users are able to share symbols between pages and when updating symbols the changes apply to all places where you have the symbol.

@artf
Copy link
Member Author

artf commented Feb 16, 2022

@abozhinov current Symbols already work in that way, I just didn't have time to create public API and properly document them.

@abozhinov
Copy link

Can you share some sample code if you have one? In my case user should have the option to create a symbol and to store this symbol in the database. After that have a list with all symbols and to dropping the symbol inside the page where you want.

@abozhinov
Copy link

@artf I start playing with Symbols but without success. https://codepen.io/abozhinov/pen/rNpNQBq just use your example with the last version of GrapesJS.

@abozhinov
Copy link

abozhinov commented Mar 15, 2022

@artf can you give me some advice on how to make it works.

@artf
Copy link
Member Author

artf commented Mar 16, 2022

@abozhinov Symbols are internally disabled at the moment, if you want to try them you should enable them first

plugins: [
  (editor) => editor.getModel().set('symbols', true),
]

Anyway, I'd not suggest using them before having the proper public API. Right now the example doesn't store them properly as main symbols should not be added/stored inside page components.

@artf artf deleted the symbols branch December 16, 2022 08:57
@NilLlisterri NilLlisterri mentioned this pull request Nov 25, 2025
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants