From e48054725ce1d482b25114fa43f41be43b57a111 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Tue, 13 Aug 2019 18:07:06 +0200 Subject: [PATCH] deploy docs --- docs/404.html | 8 +- docs/Home.html | 22 +- docs/api/assets.html | 14 +- docs/api/block_manager.html | 12 +- docs/api/canvas.html | 12 +- docs/api/commands.html | 18 +- docs/api/component.html | 78 ++-- docs/api/components.html | 18 +- docs/api/css_composer.html | 14 +- docs/api/device_manager.html | 10 +- docs/api/editor.html | 29 +- docs/api/index.html | 8 +- docs/api/keymaps.html | 14 +- docs/api/modal_dialog.html | 10 +- docs/api/panels.html | 14 +- docs/api/rich_text_editor.html | 20 +- docs/api/selector_manager.html | 16 +- docs/api/storage_manager.html | 16 +- docs/api/style_manager.html | 17 +- docs/api/undo_manager.html | 10 +- docs/assets/css/0.styles.82db7b1e.css | 1 - docs/assets/css/styles.841d52ad.css | 1 + .../js/{6.9af8fc3b.js => 1.d88ab8c2.js} | 2 +- docs/assets/js/10.6d1da19c.js | 1 + docs/assets/js/10.a106a5cd.js | 1 - docs/assets/js/11.e1024e3c.js | 1 + docs/assets/js/11.e1d01d7f.js | 1 - .../js/{12.7b5df652.js => 12.ea2d0633.js} | 2 +- .../js/{13.2c86d6bb.js => 13.0aca6e95.js} | 2 +- docs/assets/js/14.3a556795.js | 1 + docs/assets/js/14.47aa7392.js | 1 - docs/assets/js/15.b8e65128.js | 1 + docs/assets/js/15.e5ae77bc.js | 1 - docs/assets/js/16.07b42741.js | 1 - docs/assets/js/16.9e8d0ee3.js | 1 + docs/assets/js/17.2a67de0b.js | 1 - docs/assets/js/17.fea2549e.js | 1 + docs/assets/js/18.112c9d7a.js | 1 + docs/assets/js/18.11c93f1e.js | 1 - docs/assets/js/19.7a13abf6.js | 1 - docs/assets/js/19.da3eb438.js | 1 + .../js/{3.651320c9.js => 2.3f947d80.js} | 2 +- docs/assets/js/20.abef129a.js | 1 + docs/assets/js/20.d98c3b22.js | 1 - docs/assets/js/21.3e3438bd.js | 1 - docs/assets/js/21.45665626.js | 1 + docs/assets/js/22.917f34a1.js | 1 - docs/assets/js/22.f3f10b93.js | 1 + docs/assets/js/23.85818d14.js | 1 + docs/assets/js/23.a1fe2201.js | 1 - docs/assets/js/24.6618a793.js | 1 + docs/assets/js/24.fefe1628.js | 1 - docs/assets/js/25.32714364.js | 1 - docs/assets/js/25.3fa3e444.js | 1 + docs/assets/js/26.0ba924f8.js | 1 - docs/assets/js/26.19837d20.js | 1 + docs/assets/js/27.442eb105.js | 1 - docs/assets/js/27.49115d17.js | 1 + docs/assets/js/28.0cb791cd.js | 1 + docs/assets/js/28.3d0efde8.js | 1 - docs/assets/js/29.60349f33.js | 1 + docs/assets/js/29.81c2faa9.js | 1 - .../js/{4.ccc41891.js => 3.58555975.js} | 2 +- docs/assets/js/30.331e996f.js | 1 - docs/assets/js/30.4f7b4a19.js | 1 + docs/assets/js/31.3f9b4c2f.js | 1 + docs/assets/js/31.456da170.js | 1 - docs/assets/js/32.2f2eecfd.js | 1 - docs/assets/js/32.2fb15c2c.js | 1 + docs/assets/js/33.10f2e2c4.js | 1 + docs/assets/js/33.5a21eeb2.js | 1 - docs/assets/js/34.b00a201b.js | 1 + docs/assets/js/34.e495786f.js | 1 - docs/assets/js/35.842b3faa.js | 1 + docs/assets/js/35.e59a311f.js | 1 - docs/assets/js/36.8c67e6e8.js | 1 - docs/assets/js/36.cf215dd2.js | 1 + docs/assets/js/37.1f4e9422.js | 1 + docs/assets/js/37.ea48a200.js | 1 - docs/assets/js/38.41db2434.js | 1 + docs/assets/js/38.49bb9e38.js | 1 - docs/assets/js/39.2eeb020a.js | 1 - docs/assets/js/39.ff6ba2ff.js | 1 + .../js/{7.224c3fec.js => 4.c61ce6cc.js} | 2 +- docs/assets/js/40.2afed73e.js | 1 + docs/assets/js/40.5c633dc6.js | 1 - docs/assets/js/41.17bf3b3b.js | 1 - docs/assets/js/41.8a6c4d0c.js | 1 + docs/assets/js/42.ac8b349a.js | 1 + docs/assets/js/42.b82b73fe.js | 1 - docs/assets/js/43.24b80d23.js | 1 - docs/assets/js/43.53781928.js | 1 + docs/assets/js/44.ee1dfbc2.js | 1 + .../js/{2.434af1ad.js => 5.763a4c86.js} | 2 +- docs/assets/js/6.06855135.js | 1 + .../js/{5.20d1ecdf.js => 7.de412c56.js} | 2 +- docs/assets/js/8.2eb536bf.js | 1 + docs/assets/js/8.e9d59c3e.js | 1 - docs/assets/js/9.7883b5a7.js | 1 + docs/assets/js/9.9f34d8b5.js | 1 - docs/assets/js/app.841d52ad.js | 8 + docs/assets/js/app.a18f26bb.js | 8 - docs/default-link-comp.jpg | Bin 0 -> 7246 bytes docs/docs-init-link-trait.jpg | Bin 0 -> 5621 bytes docs/docs-link-trait-raw.jpg | Bin 0 -> 8865 bytes docs/faq.html | 8 +- docs/getting-started.html | 48 +-- docs/guides/Custom-CSS-parser.html | 24 +- docs/guides/Replace-Rich-Text-Editor.html | 16 +- docs/index.html | 8 +- docs/modules/Assets.html | 40 +- docs/modules/Blocks.html | 16 +- docs/modules/Commands.html | 38 +- docs/modules/Components-js.html | 34 +- docs/modules/Components-new.html | 361 ++++++++++++++++ docs/modules/Components.html | 64 +-- docs/modules/Plugins.html | 24 +- docs/modules/Storage.html | 34 +- docs/modules/Style-manager.html | 8 +- docs/modules/Traits.html | 391 ++++++++++++++---- 120 files changed, 1094 insertions(+), 484 deletions(-) delete mode 100644 docs/assets/css/0.styles.82db7b1e.css create mode 100644 docs/assets/css/styles.841d52ad.css rename docs/assets/js/{6.9af8fc3b.js => 1.d88ab8c2.js} (70%) create mode 100644 docs/assets/js/10.6d1da19c.js delete mode 100644 docs/assets/js/10.a106a5cd.js create mode 100644 docs/assets/js/11.e1024e3c.js delete mode 100644 docs/assets/js/11.e1d01d7f.js rename docs/assets/js/{12.7b5df652.js => 12.ea2d0633.js} (97%) rename docs/assets/js/{13.2c86d6bb.js => 13.0aca6e95.js} (86%) create mode 100644 docs/assets/js/14.3a556795.js delete mode 100644 docs/assets/js/14.47aa7392.js create mode 100644 docs/assets/js/15.b8e65128.js delete mode 100644 docs/assets/js/15.e5ae77bc.js delete mode 100644 docs/assets/js/16.07b42741.js create mode 100644 docs/assets/js/16.9e8d0ee3.js delete mode 100644 docs/assets/js/17.2a67de0b.js create mode 100644 docs/assets/js/17.fea2549e.js create mode 100644 docs/assets/js/18.112c9d7a.js delete mode 100644 docs/assets/js/18.11c93f1e.js delete mode 100644 docs/assets/js/19.7a13abf6.js create mode 100644 docs/assets/js/19.da3eb438.js rename docs/assets/js/{3.651320c9.js => 2.3f947d80.js} (71%) create mode 100644 docs/assets/js/20.abef129a.js delete mode 100644 docs/assets/js/20.d98c3b22.js delete mode 100644 docs/assets/js/21.3e3438bd.js create mode 100644 docs/assets/js/21.45665626.js delete mode 100644 docs/assets/js/22.917f34a1.js create mode 100644 docs/assets/js/22.f3f10b93.js create mode 100644 docs/assets/js/23.85818d14.js delete mode 100644 docs/assets/js/23.a1fe2201.js create mode 100644 docs/assets/js/24.6618a793.js delete mode 100644 docs/assets/js/24.fefe1628.js delete mode 100644 docs/assets/js/25.32714364.js create mode 100644 docs/assets/js/25.3fa3e444.js delete mode 100644 docs/assets/js/26.0ba924f8.js create mode 100644 docs/assets/js/26.19837d20.js delete mode 100644 docs/assets/js/27.442eb105.js create mode 100644 docs/assets/js/27.49115d17.js create mode 100644 docs/assets/js/28.0cb791cd.js delete mode 100644 docs/assets/js/28.3d0efde8.js create mode 100644 docs/assets/js/29.60349f33.js delete mode 100644 docs/assets/js/29.81c2faa9.js rename docs/assets/js/{4.ccc41891.js => 3.58555975.js} (67%) delete mode 100644 docs/assets/js/30.331e996f.js create mode 100644 docs/assets/js/30.4f7b4a19.js create mode 100644 docs/assets/js/31.3f9b4c2f.js delete mode 100644 docs/assets/js/31.456da170.js delete mode 100644 docs/assets/js/32.2f2eecfd.js create mode 100644 docs/assets/js/32.2fb15c2c.js create mode 100644 docs/assets/js/33.10f2e2c4.js delete mode 100644 docs/assets/js/33.5a21eeb2.js create mode 100644 docs/assets/js/34.b00a201b.js delete mode 100644 docs/assets/js/34.e495786f.js create mode 100644 docs/assets/js/35.842b3faa.js delete mode 100644 docs/assets/js/35.e59a311f.js delete mode 100644 docs/assets/js/36.8c67e6e8.js create mode 100644 docs/assets/js/36.cf215dd2.js create mode 100644 docs/assets/js/37.1f4e9422.js delete mode 100644 docs/assets/js/37.ea48a200.js create mode 100644 docs/assets/js/38.41db2434.js delete mode 100644 docs/assets/js/38.49bb9e38.js delete mode 100644 docs/assets/js/39.2eeb020a.js create mode 100644 docs/assets/js/39.ff6ba2ff.js rename docs/assets/js/{7.224c3fec.js => 4.c61ce6cc.js} (71%) create mode 100644 docs/assets/js/40.2afed73e.js delete mode 100644 docs/assets/js/40.5c633dc6.js delete mode 100644 docs/assets/js/41.17bf3b3b.js create mode 100644 docs/assets/js/41.8a6c4d0c.js create mode 100644 docs/assets/js/42.ac8b349a.js delete mode 100644 docs/assets/js/42.b82b73fe.js delete mode 100644 docs/assets/js/43.24b80d23.js create mode 100644 docs/assets/js/43.53781928.js create mode 100644 docs/assets/js/44.ee1dfbc2.js rename docs/assets/js/{2.434af1ad.js => 5.763a4c86.js} (72%) create mode 100644 docs/assets/js/6.06855135.js rename docs/assets/js/{5.20d1ecdf.js => 7.de412c56.js} (68%) create mode 100644 docs/assets/js/8.2eb536bf.js delete mode 100644 docs/assets/js/8.e9d59c3e.js create mode 100644 docs/assets/js/9.7883b5a7.js delete mode 100644 docs/assets/js/9.9f34d8b5.js create mode 100644 docs/assets/js/app.841d52ad.js delete mode 100644 docs/assets/js/app.a18f26bb.js create mode 100644 docs/default-link-comp.jpg create mode 100644 docs/docs-init-link-trait.jpg create mode 100644 docs/docs-link-trait-raw.jpg create mode 100644 docs/modules/Components-new.html diff --git a/docs/404.html b/docs/404.html index e74fe88ea..58c3ab67f 100644 --- a/docs/404.html +++ b/docs/404.html @@ -8,12 +8,12 @@ - - - + + +

404

There's nothing here.
Take me home.
- + diff --git a/docs/Home.html b/docs/Home.html index 3aac5e9ae..46abce83f 100644 --- a/docs/Home.html +++ b/docs/Home.html @@ -8,9 +8,9 @@ - - - + + +
GitHub

Getting started

This page will introduce you to the main options of GrapesJS and how it works, in the way to be able to create your custom editor.

The pretty minimalistic way to instantiate the editor could be like this:

<link rel="stylesheet" href="path/to/grapes.min.css">
-<script src="path/to/grapes.min.js"></script>
+<script src="path/to/grapes.min.js"></script>
 
 <div id="gjs"></div>
 
-<script type="text/javascript">
+<script type="text/javascript">
   var editor = grapesjs.init({
       container : '#gjs',
       components: '<div class="txt-red">Hello world!</div>',
       style: '.txt-red{color: red}',
   });
-</script>
+</script>
 

In just few lines, with the default configurations, you're already able to see something with which play around.

[[img/default-gjs.jpg]]

You'll see components commands on top left position that come handy to create and manage your blocks, below there are options which need to highlight and export them. When you select components ('mouse pointer' icon), on the right side, you should see pop up Class Manager and Style Manager options which allow to customize the style of the components. There is also a Layer Manager/Navigator ('hamburger' icon) which helps to manage easily the structure.

Of course all those stuff (panels, buttons, commands, etc.) are set just as default so you can overwrite them and add more other. Before you start to create things you should know that GrapesJS UI is composed basically by a canvas (where you will 'draw') and panels (which will contain buttons)

[[img/canvas-panels.jpg]]

If you'd like to extend the already instantiated editor you have to check API Reference. Check also how to create plugins using the same API. In this guide we'll focus on how to initialize the editor with all custom UI from scratch.

Let's start the editor with some basic toolbar panel

...
 var editor = grapesjs.init({
@@ -80,13 +80,13 @@ In this guide we'll focus on how to initialize the editor with all custom UI fro
     defaults: [{
         id: 'helloWorld',
 
-        run:  function(editor, senderBtn){
+        run:  function(editor, senderBtn){
           alert('Hello world!');
           // Deactivate button
-          senderBtn.set('active', false);
+          senderBtn.set('active', false);
         },
 
-        stop:  function(editor, senderBtn){
+        stop:  function(editor, senderBtn){
         },
     }]
   }
@@ -323,13 +323,13 @@ The default one is the localStorage which is pretty simple and all the data are
   commands: {
     defaults: [{
         id: 'storeData',
-        run:  function(editor, senderBtn){
+        run:  function(editor, senderBtn){
           editor.store();
         },
     }]
   }
 ...
 

Check Storage Manager API Reference

Last Updated: 7/13/2018, 12:12:35 AM
- + diff --git a/docs/api/assets.html b/docs/api/assets.html index c6843a284..9b7fac77a 100644 --- a/docs/api/assets.html +++ b/docs/api/assets.html @@ -8,9 +8,9 @@ - - - + + +

Returns Model

get

Returns the asset by URL

Parameters

Examples

var asset = assetManager.get('http://img.jpg');
+

Returns Model

get

Returns the asset by URL

Parameters

Examples

var asset = assetManager.get('http://img.jpg');
 

Returns Object Object representing the asset

getAll

Return the global collection, containing all the assets

Returns Collection

getAllVisible

Return the visible collection, which containes assets actually rendered

Returns Collection

remove

Remove the asset by its URL

Parameters

Examples

assetManager.remove('http://img.jpg');
 

Returns this

store

Store assets data to the selected storage

Parameters

Examples

var assets = assetManager.store();
 

Returns Object Data to store

load

Load data from the passed object. @@ -63,7 +63,7 @@ assetManager.// Render some of the assets const assets = assetManager.getAll(); assetManager.render(assets.filter( - asset => asset.get('category') == 'cats' + asset => asset.get('category') == 'cats' ));

Returns HTMLElement

addType

Add new type. If you want to get more about type definition we suggest to read the module's page

Parameters

Examples

assetManager.addType('my-type', {
  model: {},
  view: {},
- isType: (value) => {},
+ isType: (value) => {},
 })
 

getType

Get type

Parameters

Returns Object Type definition

getTypes

Get types

Returns Array

Last Updated: 7/8/2018, 1:46:22 PM
- + diff --git a/docs/api/canvas.html b/docs/api/canvas.html index a4f6b969b..15a9c11b8 100644 --- a/docs/api/canvas.html +++ b/docs/api/canvas.html @@ -8,9 +8,9 @@ - - - + + +

Once the editor is instantiated you can use its API. Before using these methods you should get the module from the instance

getConfig

Get the configuration object

Returns Object

getElement

Get the canvas element

Returns HTMLElement

getFrameEl

Get the iframe element of the canvas

Returns HTMLIFrameElement

getWindow

Get the window instance of the iframe element

Returns Window

getDocument

Get the document of the iframe element

Returns HTMLDocument

getBody

Get the body of the iframe element

Returns HTMLBodyElement

getWrapperEl

Get the wrapper element containing all the components

Returns HTMLElement

setCustomBadgeLabel

Set custom badge naming strategy

Parameters

Examples

canvas.setCustomBadgeLabel(function(component){
+

getConfig

Get the configuration object

Returns Object

getElement

Get the canvas element

Returns HTMLElement

getFrameEl

Get the iframe element of the canvas

Returns HTMLIFrameElement

getWindow

Get the window instance of the iframe element

Returns Window

getDocument

Get the document of the iframe element

Returns HTMLDocument

getBody

Get the body of the iframe element

Returns HTMLBodyElement

getWrapperEl

Get the wrapper element containing all the components

Returns HTMLElement

setCustomBadgeLabel

Set custom badge naming strategy

Parameters

Examples

canvas.setCustomBadgeLabel(function(component){
  return component.getName();
 });
 

getRect

Get canvas rectangular data

Returns Object

hasFocus

Check if the canvas is focused

Returns Boolean

scrollTo

Scroll canvas to the element if it's not visible. The scrolling is @@ -45,13 +45,13 @@ passed to it. For instance, you can scroll smoothly by using canvas.scrollTo(selected, { behavior: 'smooth' }); // Force the scroll, even if the element is alredy visible canvas.scrollTo(selected, { force: true }); -

setZoom

Set zoom value

Parameters

Returns this

getZoom

Get zoom value

Returns Number

Last Updated: 3/11/2019, 6:17:19 PM

setZoom

Set zoom value

Parameters

Returns this

getZoom

Get zoom value

Returns Number

Last Updated: 5/2/2019, 1:28:33 AM
- + diff --git a/docs/api/commands.html b/docs/api/commands.html index fe10a07d2..603cc9ab6 100644 --- a/docs/api/commands.html +++ b/docs/api/commands.html @@ -8,9 +8,9 @@ - - - + + +

add

Add new command to the collection

Parameters

Examples

commands.add('myCommand', {
-	run(editor, sender) {
+	run(editor, sender) {
 		alert('Hello world!');
 	},
-	stop(editor, sender) {
+	stop(editor, sender) {
 	},
 });
 // As a function
-commands.add('myCommand2', editor => { ... });
-

Returns this

get

Get command by ID

Parameters

Examples

var myCommand = commands.get('myCommand');
+commands.add('myCommand2', editor => { ... });
+

Returns this

get

Get command by ID

Parameters

Examples

var myCommand = commands.get('myCommand');
 myCommand.run();
 

Returns Object Object representing the command

extend

Extend the command. The command to extend should be defined as an object

Parameters

Examples

commands.extend('old-command', {
  someInnerFunction() {
@@ -64,13 +64,13 @@ commands.isA
 // -> false
 

Returns Boolean

getActive

Get all active commands

Examples

console.log(commands.getActive());
 // -> { someCommand: itsLastReturn, anotherOne: ... };
-

Returns Object

Last Updated: 8/26/2018, 2:24:48 PM

Returns Object

Last Updated: 5/2/2019, 1:28:33 AM
- + diff --git a/docs/api/component.html b/docs/api/component.html index fc42bcefb..ed7fc577f 100644 --- a/docs/api/component.html +++ b/docs/api/component.html @@ -8,9 +8,9 @@ - - - + + +
GitHub -

Component

The Component object represents a single node of our template structure, so when you update its properties the changes are immediately reflected on the canvas and in the code to export (indeed, when you ask to export the code we just go through all the tree of nodes). -An example on how to update properties:

component.set({
+An example on how to update properties:

component.set({
  tagName: 'span',
  attributes: { ... },
  removable: false,
 });
-component.get('tagName');
+component.get('tagName');
 // -> 'span'
 

Properties

  • typeString? Component type, eg. text, image, video, etc.
  • tagNameString? HTML tag of the component, eg. span. Default: div
  • attributesObject? Key-value object of the component's attributes, eg. { title: 'Hello' } Default: {}
  • nameString? Name of the component. Will be used, for example, in Layers and badges
  • removableBoolean? When true the component is removable from the canvas, default: true
  • draggable(Boolean | String)? Indicates if it's possible to drag the component inside others. You can also specify a query string to indentify elements, @@ -43,48 +43,50 @@ eg. '.some-class[title=Hello], [data-gjs-type=column]' means you ca containing some-class class and Hello title, and column components. Default: true
  • droppable(Boolean | String)? Indicates if it's possible to drop other components inside. You can use a query string as with draggable. Default: true
  • badgableBoolean? Set to false if you don't want to see the badge (with the name) over the component. Default: true
  • stylable(Boolean | Array<String>)? True if it's possible to style the component. You can also indicate an array of CSS properties which is possible to style, eg. ['color', 'width'], all other properties -will be hidden from the style manager. Default: true
  • stylable-requireArray<String>? Indicate an array of style properties to show up which has been marked as toRequire. Default: []
  • unstylableArray<String>? Indicate an array of style properties which should be hidden from the style manager. Default: []
  • style-signatureArray<String>? This option comes handy when you need to remove or export strictly component-specific rules. Be default, if this option is not empty, the editor will remove rules when there are no components, of that type, in the canvas. Eg. '['.navbar', '[navbar-']'. Default: ''
  • highlightableBoolean? It can be highlighted with 'dotted' borders if true. Default: true
  • copyableBoolean? True if it's possible to clone the component. Default: true
  • resizableBoolean? Indicates if it's possible to resize the component. It's also possible to pass an object as options for the Resizer. Default: false
  • editableBoolean? Allow to edit the content of the component (used on Text components). Default: false
  • layerableBoolean? Set to false if you need to hide the component inside Layers. Default: true
  • selectableBoolean? Allow component to be selected when clicked. Default: true
  • hoverableBoolean? Shows a highlight outline when hovering on the element if true. Default: true
  • voidBoolean? This property is used by the HTML exporter as void elements don't have closing tags, eg. <br/>, <hr/>, etc. Default: false
  • contentString? Content of the component (not escaped) which will be appended before children rendering. Default: ''
  • iconString? Component's icon, this string will be inserted before the name (in Layers and badge), eg. it can be an HTML string ''. Default: ''
  • script(String | Function)? Component's javascript. More about it here. Default: ''
  • traitsArray<(Object | String)>? Component's traits. More about it here. Default: ['id', 'title']
  • propagateArray<String>? Indicates an array of properties which will be inhereted by all NEW appended children. +will be hidden from the style manager. Default: true
  • stylable-requireArray<String>? Indicate an array of style properties to show up which has been marked as toRequire. Default: []
  • unstylableArray<String>? Indicate an array of style properties which should be hidden from the style manager. Default: []
  • style-signatureArray<String>? This option comes handy when you need to remove or export strictly component-specific rules. Be default, if this option is not empty, the editor will remove rules when there are no components, of that type, in the canvas. Eg. '['.navbar', '[navbar-']'. Default: ''
  • highlightableBoolean? It can be highlighted with 'dotted' borders if true. Default: true
  • copyableBoolean? True if it's possible to clone the component. Default: true
  • resizableBoolean? Indicates if it's possible to resize the component. It's also possible to pass an object as options for the Resizer. Default: false
  • editableBoolean? Allow to edit the content of the component (used on Text components). Default: false
  • layerableBoolean? Set to false if you need to hide the component inside Layers. Default: true
  • selectableBoolean? Allow component to be selected when clicked. Default: true
  • hoverableBoolean? Shows a highlight outline when hovering on the element if true. Default: true
  • voidBoolean? This property is used by the HTML exporter as void elements don't have closing tags, eg. <br/>, <hr/>, etc. Default: false
  • contentString? Content of the component (not escaped) which will be appended before children rendering. Default: ''
  • iconString? Component's icon, this string will be inserted before the name (in Layers and badge), eg. it can be an HTML string ''. Default: ''
  • script(String | Function)? Component's javascript. More about it here. Default: ''
  • script-export(String | Function)? You can specify javascript available only in export functions (eg. when you get the HTML). +If this property is defined it will overwrite the script one (in export functions). Default: ''
  • traitsArray<(Object | String)>? Component's traits. More about it here. Default: ['id', 'title']
  • propagateArray<String>? Indicates an array of properties which will be inhereted by all NEW appended children. For example if you create a component likes this: { removable: false, draggable: false, propagate: ['removable', 'draggable'] } and append some new component inside, the new added component will get the exact same properties indicated in the propagate array (and the propagate property itself). Default: []
  • toolbarArray<Object>? Set an array of items to show up inside the toolbar when the component is selected (move, clone, delete). Eg. toolbar: [ { attributes: {class: 'fa fa-arrows'}, command: 'tlb-move' }, ... ]. By default, when toolbar property is falsy the editor will add automatically commands like move, delete, etc. based on its properties.
  • componentsCollection<Component>? Children components. Default: null

init

Hook method, called once the model is created

updated

Hook method, called when the model has been updated (eg. updated some model's property)

Parameters

  • propertyString Property name, if triggered after some property update
  • valueany Property value, if triggered after some property update
  • previousany Property previous value, if triggered after some property update

removed

Hook method, called once the model has been removed

is

Check component's type

Parameters

Examples

component.is('image')
 // -> false
-

Returns Boolean

index

Get the index of the component in the parent collection.

Returns Number

find

Find inner components by query string. -ATTENTION: this method works only with already rendered component

Parameters

Examples

component.find('div > .class');
+

Returns Boolean

props

Return all the propeties

Returns Object

index

Get the index of the component in the parent collection.

Returns Number

setDragMode

Change the drag mode of the component. +To get more about this feature read: https://github.com/artf/grapesjs/issues/1936

Parameters

  • valueString Drag mode, options: 'absolute' | 'translate'

Returns this

find

Find inner components by query string. +ATTENTION: this method works only with already rendered component

Parameters

Examples

component.find('div > .class');
 // -> [Component, Component, ...]
 

Returns Array Array of components

findType

Find all inner components by component id. The advantage of this method over find is that you can use it -also before rendering the component

Parameters

Examples

const allImages = component.findType('image');
+also before rendering the component

Parameters

Examples

const allImages = component.findType('image');
 console.log(allImages[0]) // prints the first found component
 

Returns Array<Component>

closest

Find the closest parent component by query string. -ATTENTION: this method works only with already rendered component

Parameters

Examples

component.closest('div.some-class');
+ATTENTION: this method works only with already rendered component

Parameters

Examples

component.closest('div.some-class');
 // -> Component
-

Returns Component

replaceWith

Replace a component with another one

Parameters

Examples

component.replaceWith('<div>Some new content</div>');
+

Returns Component

replaceWith

Replace a component with another one

Parameters

Examples

component.replaceWith('<div>Some new content</div>');
 // -> Component
-

Returns (Component | Array<Component>) New added component/s

setAttributes

Update attributes of the component

Parameters

  • attrsObject Key value attributes
  • opts (optional, default {})

Examples

component.setAttributes({ id: 'test', 'data-key': 'value' });
-

Returns this

addAttributes

Add attributes to the component

Parameters

  • attrsObject Key value attributes

Examples

component.addAttributes({ 'data-key': 'value' });
-

Returns this

getStyle

Get the style of the component

Returns Object

setStyle

Set the style on the component

Parameters

  • propObject Key value style object (optional, default {})
  • opts (optional, default {})

Examples

component.setStyle({ color: 'red' });
-

Returns Object

getAttributes

Return all component's attributes

Returns Object

addClass

Add classes

Parameters

Examples

model.addClass('class1');
+

Returns (Component | Array<Component>) New added component/s

setAttributes

Update attributes of the component

Parameters

  • attrsObject Key value attributes
  • opts (optional, default {})

Examples

component.setAttributes({ id: 'test', 'data-key': 'value' });
+

Returns this

addAttributes

Add attributes to the component

Parameters

  • attrsObject Key value attributes

Examples

component.addAttributes({ 'data-key': 'value' });
+

Returns this

getStyle

Get the style of the component

Returns Object

setStyle

Set the style on the component

Parameters

  • propObject Key value style object (optional, default {})
  • opts (optional, default {})

Examples

component.setStyle({ color: 'red' });
+

Returns Object

getAttributes

Return all component's attributes

Returns Object

addClass

Add classes

Parameters

Examples

model.addClass('class1');
 model.addClass('class1 class2');
 model.addClass(['class1', 'class2']);
 // -> [SelectorObject, ...]
-

Returns Array Array of added selectors

setClass

Set classes (resets current collection)

Parameters

Examples

model.setClass('class1');
+

Returns Array Array of added selectors

setClass

Set classes (resets current collection)

Parameters

Examples

model.setClass('class1');
 model.setClass('class1 class2');
 model.setClass(['class1', 'class2']);
 // -> [SelectorObject, ...]
-

Returns Array Array of added selectors

removeClass

Remove classes

Parameters

Examples

model.removeClass('class1');
+

Returns Array Array of added selectors

removeClass

Remove classes

Parameters

Examples

model.removeClass('class1');
 model.removeClass('class1 class2');
 model.removeClass(['class1', 'class2']);
 // -> [SelectorObject, ...]
-

Returns Array Array of removed selectors

getClasses

Returns component's classes as an array of strings

Returns Array

append

Add new component children

Parameters

  • components(Component | String) Component to add
  • optsObject Options, same as in model.add()(from backbone) (optional, default {})

Examples

someComponent.get('components').length // -> 0
+

Returns Array Array of removed selectors

getClasses

Returns component's classes as an array of strings

Returns Array

append

Add new component children

Parameters

  • components(Component | String) Component to add
  • optsObject Options, same as in model.add()(from backbone) (optional, default {})

Examples

someComponent.get('components').length // -> 0
 const videoComponent = someComponent.append('<video></video><div></div>')[0];
 // This will add 2 components (`video` and `div`) to your `someComponent`
-someComponent.get('components').length // -> 2
+someComponent.get('components').length // -> 2
 // You can pass components directly
 otherComponent.append(otherComponent2);
 otherComponent.append([otherComponent3, otherComponent4]);
 

Returns Array Array of appended components

components

Set new collection if components are provided, otherwise the -current collection is returned

Parameters

Examples

// Set new collection
+current collection is returned

Parameters

Examples

// Set new collection
 component.components('<span></span><div></div>');
 // Get current collection
 const collection = component.components();
@@ -92,27 +94,27 @@ console.log<
 // -> 2
 

Returns (Collection | Array<Component>)

parent

Get the parent component, if exists

Examples

component.parent();
 // -> Component
-

Returns Component

getTrait

Get the trait by id/name

Parameters

  • idString The id or name of the trait

Examples

const traitTitle = component.getTrait('title');
-traitTitle && traitTitle.set('label', 'New label');
-

Returns Trait Trait model

updateTrait

Update a trait

Parameters

  • idString The id or name of the trait
  • propsObject Object with the props to update

Examples

component.updateTrait('title', {
+

Returns Component

getTrait

Get the trait by id/name

Parameters

  • idString The id or name of the trait

Examples

const traitTitle = component.getTrait('title');
+traitTitle && traitTitle.set('label', 'New label');
+

Returns Trait Trait model

updateTrait

Update a trait

Parameters

  • idString The id or name of the trait
  • propsObject Object with the props to update

Examples

component.updateTrait('title', {
  type: 'select',
  options: [ 'Option 1', 'Option 2' ],
 });
 

Returns this

getTraitIndex

Get the trait position index by id/name. Useful in case you want to -replace some trait, at runtime, with something else.

Parameters

  • idString The id or name of the trait

Examples

const traitTitle = component.getTraitIndex('title');
+replace some trait, at runtime, with something else.

Parameters

  • idString The id or name of the trait

Examples

const traitTitle = component.getTraitIndex('title');
 console.log(traitTitle); // 1
-

Returns Number Index position of the current trait

removeTrait

Remove trait/s by id/s.

Parameters

Examples

component.removeTrait('title');
+

Returns Number Index position of the current trait

removeTrait

Remove trait/s by id/s.

Parameters

Examples

component.removeTrait('title');
 component.removeTrait(['title', 'id']);
-

Returns Array Array of removed traits

addTrait

Add trait/s by id/s.

Parameters

Examples

component.addTrat('title', { at: 1 }); // Add title trait (`at` option is the position index)
-component.addTrat({
+

Returns Array Array of removed traits

addTrait

Add trait/s by id/s.

Parameters

Examples

component.addTrait('title', { at: 1 }); // Add title trait (`at` option is the position index)
+component.addTrait({
  type: 'checkbox',
  name: 'disabled',
 });
-component.addTrat(['title', {...}, ...]);
-

Returns Array Array of added traits

getName

Get the name of the component

Returns String

getIcon

Get the icon string

Returns String

toHTML

Return HTML string of the component

Parameters

  • optsObject Options (optional, default {}) +component.addTrait(['title', {...}, ...]); +

Returns Array Array of added traits

getName

Get the name of the component

Returns String

getIcon

Get the icon string

Returns String

toHTML

Return HTML string of the component

Parameters

  • optsObject Options (optional, default {})
    • opts.attributes(Object | Function) You can pass an object of custom attributes to replace with the current one or you can even pass a function to generate attributes dynamically (optional, default null)

Examples

// Simple HTML return
-component.set({ tagName: 'span' });
+component.set({ tagName: 'span' });
 component.setAttributes({ title: 'Hello' });
 component.toHTML();
 // -> <span title="Hello"></span>
@@ -123,30 +125,30 @@ component.to
 
 // Custom dynamic attributes
 component.toHTML({
- attributes(component, attributes) {
-   if (component.get('tagName') == 'span') {
+ attributes(component, attributes) {
+   if (component.get('tagName') == 'span') {
      attributes.title = 'Custom attribute';
    }
    return attributes;
  },
 });
 // -> <span title="Custom attribute"></span>
-

Returns String HTML string

getId

Return the component id

Returns String

setId

Set new id on the component

Parameters

Returns this

getEl

Get the DOM element of the component. +

Returns String HTML string

getId

Return the component id

Returns String

setId

Set new id on the component

Parameters

Returns this

getEl

Get the DOM element of the component. This works only if the component is already rendered

Returns HTMLElement

getView

Get the View of the component. -This works only if the component is already rendered

Returns ComponentView

onAll

Execute callback function on itself and all inner components

Parameters

  • clbFunction Callback function, the model is passed as an argument

Examples

component.onAll(component => {
+This works only if the component is already rendered

Returns ComponentView

onAll

Execute callback function on itself and all inner components

Parameters

  • clbFunction Callback function, the model is passed as an argument

Examples

component.onAll(component => {
  // do something with component
 })
 

Returns this

remove

Remove the component

Returns this

getList

The list of components is taken from the Components module. Initially, the list, was set statically on the Component object but it was -not ok, as it was shared between multiple editor instances

Parameters

  • model

checkId

This method checks, for each parsed component and style object +not ok, as it was shared between multiple editor instances

Parameters

  • model

checkId

This method checks, for each parsed component and style object (are not Components/CSSRules yet), for duplicated id and fixes them -This method is used in Components.js just after the parsing

Parameters

  • components
  • styles (optional, default [])
  • list (optional, default {})
Last Updated: 3/11/2019, 6:17:19 PM
Last Updated: 5/2/2019, 1:28:33 AM
- + diff --git a/docs/api/components.html b/docs/api/components.html index 26ae9499f..e30339fc6 100644 --- a/docs/api/components.html +++ b/docs/api/components.html @@ -8,9 +8,9 @@ - - - + + +

DomComponents

With this module is possible to manage components inside the canvas. You can customize the initial state of the module from the editor initialization, by passing the following Configuration Object

DomComponents

With this module is possible to manage components inside the canvas. You can customize the initial state of the module from the editor initialization, by passing the following Configuration Object

const editor = grapesjs.init({
  domComponents: {
    // options
  }
@@ -38,8 +38,8 @@ autonomously from the selected storage
 The fetched data will be added to the collection

Parameters

  • dataObject Object of data to load (optional, default '')

Returns Object Loaded data

store

Store components on the selected storage

Parameters

  • noStoreBoolean If true, won't store

Returns Object Data to store

getWrapper

Returns root component inside the canvas. Something like <body> inside HTML page The wrapper doesn't differ from the original Component Model

Examples

// Change background of the wrapper and set some attribute
 var wrapper = domComponents.getWrapper();
-wrapper.set('style', {'background-color': 'red'});
-wrapper.set('attributes', {'title': 'Hello!'});
+wrapper.set('style', {'background-color': 'red'});
+wrapper.set('attributes', {'title': 'Hello!'});
 

Returns Component Root Component

getComponents

Returns wrapper's children collection. Once you have the collection you can add other Components(Models) inside. Each component can have several nested components inside and you can nest them as more as you wish.

Examples

// Let's add some component
@@ -54,7 +54,7 @@ components inside and you can nest them as more as you wish.

// Now let's add an other one inside first component // First we have to get the collection inside. Each // component has 'components' property -var comp1Children = comp1.get('components'); +var comp1Children = comp1.get('components'); // Procede as before. You could also add multiple objects comp1Children.add([ { style: { 'background-color': 'blue'}}, @@ -79,13 +79,13 @@ Once the wrapper is rendered, and it's what happens when you init the editor, the all new components will be added automatically and property changes are all updated immediately

Returns HTMLElement

clear

Remove all components

Returns this

addType

Add new component type. Read more about this in Define New Component

Parameters

Returns this

getType

Get component type. -Read more about this in Define New Component

Parameters

Returns Object Component type defintion, eg. { model: ..., view: ... }

getTypes

Return the array of all types

Returns Array

Last Updated: 1/5/2019, 1:17:11 AM
Last Updated: 1/5/2019, 1:17:11 AM
- + diff --git a/docs/api/css_composer.html b/docs/api/css_composer.html index c451b8a5a..70f774274 100644 --- a/docs/api/css_composer.html +++ b/docs/api/css_composer.html @@ -8,9 +8,9 @@ - - - + + +

Returns Model

get

Get the rule

Parameters

  • selectorsArray<Selector> Array of selectors
  • stateString Css rule state
  • widthString For which device this style is oriented
  • rulePropsObject Other rule props

Examples

var sm = editor.SelectorManager;
 var sel1 = sm.add('myClass1');
 var sel2 = sm.add('myClass2');
-var rule = cssComposer.get([sel1, sel2], 'hover');
+var rule = cssComposer.get([sel1, sel2], 'hover');
 // Update the style
-rule.set('style', {
+rule.set('style', {
   width: '300px',
   color: '#000',
 });
@@ -79,6 +79,6 @@ console.log<
           Modal
          →
       

- + diff --git a/docs/api/device_manager.html b/docs/api/device_manager.html index 3b2b7eb7d..1e5b32c84 100644 --- a/docs/api/device_manager.html +++ b/docs/api/device_manager.html @@ -8,9 +8,9 @@ - - - + + +

Returns Device Added device

get

Return device by name

Parameters

Examples

var device = deviceManager.get('Tablet');
+

Returns Device Added device

get

Return device by name

Parameters

Examples

var device = deviceManager.get('Tablet');
 console.log(JSON.stringify(device));
 // {name: 'Tablet', width: '900px'}
 

getAll

Return all devices

Examples

var devices = deviceManager.getAll();
@@ -51,6 +51,6 @@ console.log<
           Selector Manager
          →
       

- + diff --git a/docs/api/editor.html b/docs/api/editor.html index f203b3769..3e387a41d 100644 --- a/docs/api/editor.html +++ b/docs/api/editor.html @@ -8,9 +8,9 @@ - - - + + +

Editor

Editor contains the top level API which you'll probably use to customize the editor or extend it with plugins. You get the Editor instance on init method and you can pass options via its Configuration Object

const editor = grapesjs.init({
    // options
 });
-

Available Events

You can make use of available events in this way

editor.on('EVENT-NAME', (some, argument) => {
+

Available Events

You can make use of available events in this way

editor.on('EVENT-NAME', (some, argument) => {
    // do something
 })
 

Components

  • component:create - Component is created (only the model, is not yet mounted in the canvas), called after the init() method
  • component:mount - Component is mounted to an element and rendered in canvas
  • component:add - Triggered when a new component is added to the editor, the model is passed as an argument to the callback
  • component:remove - Triggered when a component is removed, the model is passed as an argument to the callback
  • component:clone - Triggered when a component is cloned, the new model is passed as an argument to the callback
  • component:update - Triggered when a component is updated (moved, styled, etc.), the model is passed as an argument to the callback
  • component:update:{propertyName} - Listen any property change, the model is passed as an argument to the callback
  • component:styleUpdate - Triggered when the style of the component is updated, the model is passed as an argument to the callback
  • component:styleUpdate:{propertyName} - Listen for a specific style property change, the model is passed as an argument to the callback
  • component:selected - New component selected, the selected model is passed as an argument to the callback
  • component:deselected - Component deselected, the deselected model is passed as an argument to the callback
  • component:toggled - Component selection changed, toggled model is passed as an argument to the callback
  • component:type:add - New component type added, the new type is passed as an argument to the callback
  • component:type:update - Component type updated, the updated type is passed as an argument to the callback

Blocks

  • block:add - New block added
  • block:remove - Block removed
  • block:drag:start - Started dragging block, model of the block is passed as an argument
  • block:drag - Dragging block, the block's model and the drag event are passed as arguments
  • block:drag:stop - Dragging of the block is stopped. As agruments for the callback you get, the dropped component model (if dropped successfully) and the model of the block

Assets

  • asset:add - New asset added
  • asset:remove - Asset removed
  • asset:upload:start - Before the upload is started
  • asset:upload:end - After the upload is ended
  • asset:upload:error - On any error in upload, passes the error as an argument
  • asset:upload:response - On upload response, passes the result as an argument

Keymaps

  • keymap:add - New keymap added. The new keyamp object is passed as an argument
  • keymap:remove - Keymap removed. The removed keyamp object is passed as an argument
  • keymap:emit - Some keymap emitted, in arguments you get keymapId, shortcutUsed, Event
  • keymap:emit:{keymapId} - keymapId emitted, in arguments you get keymapId, shortcutUsed, Event

Style Manager

  • styleManager:update:target - The target (Component or CSSRule) is changed
  • styleManager:change - Triggered on style property change from new selected component, the view of the property is passed as an argument to the callback
  • styleManager:change:{propertyName} - As above but for a specific style property

Storages

  • storage:start - Before the storage request is started
  • storage:start:store - Before the store request. The object to store is passed as an argumnet (which you can edit)
  • storage:start:load - Before the load request. Items to load are passed as an argumnet (which you can edit)
  • storage:load - Triggered when something was loaded from the storage, loaded object passed as an argumnet
  • storage:store - Triggered when something is stored to the storage, stored object passed as an argumnet
  • storage:end - After the storage request is ended
  • storage:end:store - After the store request
  • storage:end:load - After the load request
  • storage:error - On any error on storage request, passes the error as an argument
  • storage:error:store - Error on store request, passes the error as an argument
  • storage:error:load - Error on load request, passes the error as an argument

Canvas

  • canvas:dragenter - When something is dragged inside the canvas, DataTransfer instance passed as an argument
  • canvas:dragover - When something is dragging on canvas, DataTransfer instance passed as an argument
  • canvas:drop - Something is dropped in canvas, DataTransfer instance and the dropped model are passed as arguments
  • canvas:dragend - When a drag operation is ended, DataTransfer instance passed as an argument
  • canvas:dragdata - On any dataTransfer parse, DataTransfer instance and the result are passed as arguments. @@ -53,7 +53,7 @@ editor.addCo classes:['cls'], content: 'New component' }); -

Returns (Model | Array<Model>)

getStyle

Returns style in JSON format object

Returns Object

setStyle

Set style inside editor's canvas. This method overrides actual style

Parameters

Examples

editor.setStyle('.cls{color: red}');
+

Returns Array<Component>

getStyle

Returns style in JSON format object

Returns Object

setStyle

Set style inside editor's canvas. This method overrides actual style

Parameters

Examples

editor.setStyle('.cls{color: red}');
 //or
 editor.setStyle({
   selectors: ['cls']
@@ -65,7 +65,7 @@ itself and all changes will go inside its 'style' attribute. Otherwise,
 if the selected component has one or more classes, the function will
 return the corresponding CSS Rule

Returns Model

select

Select a component

Parameters

  • el(Component | HTMLElement) Component to select
  • optsObject? Options
    • opts.scrollBoolean? Scroll canvas to the selected element

Examples

// Select dropped block
-editor.on('block:drag:stop', function(model) {
+editor.on('block:drag:stop', function(model) {
  editor.select(model);
 });
 

Returns this

selectAdd

Add component to selection

Parameters

Examples

setCustomParserCss

Replace the default CSS parser with a custom one. The parser function receives a CSS string as a parameter and expects an array of CSSRule objects as a result. If you need to remove the -custom parser, pass null as the argument

Parameters

Examples

editor.setCustomParserCss(css => {
+custom parser, pass null as the argument

Parameters

Examples

editor.setCustomParserCss(css => {
  const result = [];
  // ... parse the CSS string
  result.push({
@@ -117,20 +117,21 @@ custom parser, pass null as the argument

// ... return result; }); -

Returns this

log

Trigger event log message

Parameters

  • msgany Message to log
  • optsObject Custom options (optional, default {}) +

Returns this

setDragMode

Change the global drag mode of components. +To get more about this feature read: https://github.com/artf/grapesjs/issues/1936

Parameters

  • valueString Drag mode, options: 'absolute' | 'translate'

Returns this

log

Trigger event log message

Parameters

  • msgany Message to log
  • optsObject Custom options (optional, default {})
    • opts.nsString Namespace of the log (eg. to use in plugins) (optional, default '')
    • opts.levelString Level of the log, debug, info, warning, error (optional, default 'debug')

Examples

editor.log('Something done!', { ns: 'from-plugin-x', level: 'info' });
 // This will trigger following events
 // `log`, `log:info`, `log-from-plugin-x`, `log-from-plugin-x:info`
 // Callbacks of those events will always receive the message and
 // options, as arguments, eg:
 // editor.on('log:info', (msg, opts) => console.info(msg, opts))
-

Returns this

on

Attach event

Parameters

Returns this

once

Attach event and detach it after the first run

Parameters

Returns this

off

Detach event

Parameters

Returns this

trigger

Trigger event

Parameters

Returns this

destroy

Destroy the editor

render

Render editor

Returns HTMLElement

Last Updated: 3/11/2019, 6:17:19 PM

Returns this

on

Attach event

Parameters

Returns this

once

Attach event and detach it after the first run

Parameters

Returns this

off

Detach event

Parameters

Returns this

trigger

Trigger event

Parameters

Returns this

destroy

Destroy the editor

render

Render editor

Returns HTMLElement

Last Updated: 5/2/2019, 1:28:33 AM
- + diff --git a/docs/api/index.html b/docs/api/index.html index 47dea66af..a6a17f7c0 100644 --- a/docs/api/index.html +++ b/docs/api/index.html @@ -8,9 +8,9 @@ - - - + + + - + diff --git a/docs/api/keymaps.html b/docs/api/keymaps.html index e125b54c9..cc0810818 100644 --- a/docs/api/keymaps.html +++ b/docs/api/keymaps.html @@ -8,9 +8,9 @@ - - - + + +

Once the editor is instantiated you can use its API. Before using these methods you should get the module from the instance

getConfig

Get module configurations

Returns Object Configuration object

add

Add new keymap

Parameters

  • idstring Keymap id
  • keysstring Keymap keys, eg. ctrl+a, ⌘+z, ctrl+z
  • handler(Function | string) Keymap handler, might be a function
  • optsObject Options (optional, default {})

Examples

// 'ns' is just a custom namespace
-keymaps.add('ns:my-keymap', '⌘+j, ⌘+u, ctrl+j, alt+u', editor => {
+keymaps.add('ns:my-keymap', '⌘+j, ⌘+u, ctrl+j, alt+u', editor => {
  console.log('do stuff');
 });
 // or
 keymaps.add('ns:my-keymap', '⌘+s, ctrl+s', 'some-gjs-command');
 
 // listen to events
-editor.on('keymap:emit', (id, shortcut, e) => {
+editor.on('keymap:emit', (id, shortcut, e) => {
  // ...
 })
 

Returns Object Added keymap -or just a command id as a string

get

Get the keymap by id

Parameters

Examples

keymaps.get('ns:my-keymap');
+or just a command id as a string

get

Get the keymap by id

Parameters

Examples

keymaps.get('ns:my-keymap');
 // -> {keys, handler};
 

Returns Object Keymap object

getAll

Get all keymaps

Examples

keymaps.getAll();
 // -> {id1: {}, id2: {}};
@@ -65,6 +65,6 @@ or just a command id as a string

+ diff --git a/docs/api/modal_dialog.html b/docs/api/modal_dialog.html index b6829d790..d1ad065a0 100644 --- a/docs/api/modal_dialog.html +++ b/docs/api/modal_dialog.html @@ -8,9 +8,9 @@ - - - + + +
Last Updated: 8/10/2018, 12:36:09 AM

Returns this

getContent

Get the content of the modal window

Returns string

Last Updated: 5/2/2019, 1:28:33 AM
- + diff --git a/docs/api/panels.html b/docs/api/panels.html index 3bcc92b09..c9ff4b6b2 100644 --- a/docs/api/panels.html +++ b/docs/api/panels.html @@ -8,9 +8,9 @@ - - - + + + - + diff --git a/docs/api/rich_text_editor.html b/docs/api/rich_text_editor.html index f18bc2102..b4b9afd18 100644 --- a/docs/api/rich_text_editor.html +++ b/docs/api/rich_text_editor.html @@ -8,9 +8,9 @@ - - - + + +

Once the editor is instantiated you can use its API. Before using these methods you should get the module from the instance

add

Add a new action to the built-in RTE toolbar

Parameters

  • namestring Action name
  • actionObject Action options (optional, default {})

Examples

rte.add('bold', {
   icon: '<b>B</b>',
-  attributes: {title: 'Bold',}
-  result: rte => rte.exec('bold')
+  attributes: {title: 'Bold'},
+  result: rte => rte.exec('bold')
 });
 rte.add('link', {
   icon: document.getElementById('t'),
   attributes: {title: 'Link',}
   // Example on it's easy to wrap a selected content
-  result: rte => rte.insertHTML(`<a href="#">${rte.selection()}</a>`)
+  result: rte => rte.insertHTML(`<a href="#">${rte.selection()}</a>`)
 });
 // An example with fontSize
 rte.add('fontSize', {
@@ -54,16 +54,16 @@ rte.add,
     // Bind the 'result' on 'change' listener
   event: 'change',
-  result: (rte, action) => rte.exec('fontSize', action.btn.firstChild.value),
+  result: (rte, action) => rte.exec('fontSize', action.btn.firstChild.value),
   // Callback on any input change (mousedown, keydown, etc..)
-  update: (rte, action) => {
+  update: (rte, action) => {
     const value = rte.doc.queryCommandValue(action.name);
     if (value != 'false') { // value is a string
       action.btn.firstChild.value = value;
     }
    }
   })
-

get

Get the action by its name

Parameters

Examples

const action = rte.get('bold');
+

get

Get the action by its name

Parameters

Examples

const action = rte.get('bold');
 // {name: 'bold', ...}
 

Returns Object

getAll

Get all actions

Returns Array

remove

Remove the action from the toolbar

Parameters

Examples

const action = rte.remove('bold');
 // {name: 'bold', ...}
@@ -74,6 +74,6 @@ rte.add →
       

- + diff --git a/docs/api/selector_manager.html b/docs/api/selector_manager.html index 36acba67d..5700187d4 100644 --- a/docs/api/selector_manager.html +++ b/docs/api/selector_manager.html @@ -8,9 +8,9 @@ - - - + + +

SelectorManager

Selectors in GrapesJS are used in CSS Composer inside Rules and in Components as classes. To illustrate this concept let's take a look at this code:

span > #send-btn.btn{
  ...
 }
@@ -53,16 +53,16 @@ a look at this code:

.addClass('class1 class2');
 sm.addClass(['class1', 'class2']);
 // -> [SelectorObject, ...]
-

Returns Array Array of added selectors

get

Get the selector by its name

Parameters

Examples

const selector = selectorManager.get('selectorName');
+

Returns Array Array of added selectors

get

Get the selector by its name

Parameters

Examples

const selector = selectorManager.get('selectorName');
 // or get an array
-const selectors = selectorManager.get(['class1', 'class2']);
-

Returns (Model | Array)

getAll

Get all selectors

Returns Collection

Last Updated: 4/9/2019, 10:02:45 PM

Returns (Model | Array)

getAll

Get all selectors

Returns Collection

escapeName

Return escaped selector name

Parameters

  • nameString Selector name to escape

Returns String Escaped name

Last Updated: 4/9/2019, 10:02:45 PM
- + diff --git a/docs/api/storage_manager.html b/docs/api/storage_manager.html index 06948446f..6cf725e90 100644 --- a/docs/api/storage_manager.html +++ b/docs/api/storage_manager.html @@ -8,9 +8,9 @@ - - - + + +

Once the editor is instantiated you can use its API. Before using these methods you should get the module from the instance

getConfig

Get configuration object

Returns Object

isAutosave

Checks if autosave is enabled

Returns Boolean

setAutosave

Set autosave value

Parameters

Returns this

getStepsBeforeSave

Returns number of steps required before trigger autosave

Returns number

setStepsBeforeSave

Set steps required before trigger autosave

Parameters

Returns this

add

Add new storage

Parameters

Examples

storageManager.add('local2', {
-  load: function(keys, clb, clbErr) {
+  load: function(keys, clb, clbErr) {
     var res = {};
     for (var i = 0, len = keys.length; i < len; i++){
       var v = localStorage.getItem(keys[i]);
@@ -45,17 +45,17 @@
     // In case of errors...
     // clbErr('Went something wrong');
   },
-  store: function(data, clb, clbErr) {
+  store: function(data, clb, clbErr) {
     for(var key in data)
       localStorage.setItem(key, data[key]);
     clb(); // might be called inside some async method
   }
 });
 

Returns this

get

Returns storage by id

Parameters

Returns (Object | null)

getStorages

Returns all storages

Returns Array

getCurrent

Returns current storage type

Returns string

setCurrent

Set current storage type

Parameters

Returns this

store

Store key-value resources in the current storage

Parameters

  • dataObject Data in key-value format, eg. {item1: value1, item2: value2}
  • clbFunction Callback function

Examples

storageManager.store({item1: value1, item2: value2});
-

Returns (Object | null)

load

Load resource from the current storage by keys

Parameters

Examples

storageManager.load(['item1', 'item2'], res => {
+

Returns (Object | null)

load

Load resource from the current storage by keys

Parameters

Examples

storageManager.load(['item1', 'item2'], res => {
  // res -> {item1: value1, item2: value2}
 });
-storageManager.load('item1', res => {
+storageManager.load('item1', res => {
 // res -> {item1: value1}
 });
 

getCurrentStorage

Get current storage

Returns Storage

Last Updated: 7/8/2018, 11:25:18 PM
- + diff --git a/docs/index.html b/docs/index.html index 18e41fd5d..7aca4c5a3 100644 --- a/docs/index.html +++ b/docs/index.html @@ -8,9 +8,9 @@ - - - + + + - + diff --git a/docs/modules/Assets.html b/docs/modules/Assets.html index 3f3b88998..abee11176 100644 --- a/docs/modules/Assets.html +++ b/docs/modules/Assets.html @@ -8,9 +8,9 @@ - - - + + +

Ok, now let's show only assets form the first category

It's up to you tell the editor how to recognize your type and for this purpose you should to use isType() method. Let's see now an example of how we'd start to defining a type like svg-icon

The default open-assets command shows only image assets, so to render svg-icon run this

You should see something like this

The SVG asset is not rendered correctly and this is because we haven't yet configured its view

am.addType('svg-icon', {
   view: {
@@ -214,7 +214,7 @@ am.add// Check the base `template()` here:
     // https://github.com/artf/grapesjs/blob/dev/src/asset_manager/view/AssetView.js
     getPreview() {
-      return `<div style="text-align: center">${this.model.get('svgContent')}</div>`;
+      return `<div style="text-align: center">${this.model.get('svgContent')}</div>`;
     },
     getInfo() {
       // You can use model's properties if you passed them:
@@ -228,27 +228,27 @@ am.addreturn '<div>SVG description</div>';
     },
   },
-  isType(value) {...}
+  isType(value) {...}
 })
 

This is the result

Now we have to deal with how to assign our svgContent to the selected element

am.addType('svg-icon', {
   view: {
     // In our case the target is the selected component
-    updateTarget(target) {
-      const svg = this.model.get('svgContent');
+    updateTarget(target) {
+      const svg = this.model.get('svgContent');
 
       // Just to make things bit interesting, if it's an image type
       // I put the svg as a data uri, content otherwise
-      if (target.get('type') == 'image') {
+      if (target.get('type') == 'image') {
         // Tip: you can also use `data:image/svg+xml;utf8,<svg ...` but you
         // have to escape few chars
-        target.set('src', `data:mime/type;base64,${btoa(svg)}`);
+        target.set('src', `data:mime/type;base64,${btoa(svg)}`);
       } else {
-        target.set('content', svg);
+        target.set('content', svg);
       }
     },
     ...
   },
-  isType(value) {...}
+  isType(value) {...}
 })
 

Our custom svg-icon asset is ready to use. You can also add a model to the addType definition to group the business logic of your asset, but usually it's optional.

// Just an example of model use
 am.addType('svg-icon', {
@@ -263,11 +263,11 @@ am.addType// You can call model's methods inside views:
     // const name = this.model.getName();
     getName() {
-      return this.get('name');
+      return this.get('name');
     }
   },
   view: {...},
-  isType(value) {...}
+  isType(value) {...}
 })
 

Extend Asset Types

Extending asset types is basically the same as adding them, you can choose what type to extend and how.

// svgIconType will contain the definition (model, view, isType)
 const svgIconType = am.getType('svg-icon');
@@ -294,7 +294,7 @@ am.addType: {
     // If you want to see more methods to extend check out
     // https://github.com/artf/grapesjs/blob/dev/src/asset_manager/view/AssetImageView.js
-    onRemove(e) {
+    onRemove(e) {
       e.stopPropagation();
       const model = this.model;
 
@@ -330,13 +330,13 @@ editor.on});
 
 // Error handling
-editor.on('asset:upload:error', (err) => {
+editor.on('asset:upload:error', (err) => {
   ...
   notifyError(err);
 });
 
 // Do something on response
-editor.on('asset:upload:response', (response) => {
+editor.on('asset:upload:response', (response) => {
   ...
 });
 

Response

When the uploading is over, by default (via config parameter autoAdd: 1), the editor expects to receive a JSON blob of uploaded assets in a data key as a response and tries to add them to the main collection. The JSON might look like this:

{
@@ -367,6 +367,6 @@ editor.on →
       

- + diff --git a/docs/modules/Blocks.html b/docs/modules/Blocks.html index 4172fecc2..96a863835 100644 --- a/docs/modules/Blocks.html +++ b/docs/modules/Blocks.html @@ -8,9 +8,9 @@ - - - + + + - + diff --git a/docs/modules/Commands.html b/docs/modules/Commands.html index beaa69bcf..23ac9b53b 100644 --- a/docs/modules/Commands.html +++ b/docs/modules/Commands.html @@ -8,9 +8,9 @@ - - - + + +

For all other available options check directly the configuration source file.

Most commonly commands are created dynamically post-initialization, in that case, you'll need to use the Commands API (eg. this is what you need if you create a plugin)

const commands = editor.Commands;
-commands.add('my-command-id', editor => {
+commands.add('my-command-id', editor => {
   alert('This is my command');
 });
 
 // or it would be the same...
 commands.add('my-command-id', {
-  run(editor) {
+  run(editor) {
     alert('This is my command');
   },
 });
 

As you see the definition is quite easy, you just add an ID and the callback function. The Editor instance is passed as the first argument to the callback so you can access any other module or API method.

Now if you want to call that command you should just run this

editor.runCommand('my-command-id');
 

TIP

The method editor.runCommand is an alias of editor.Commands.run

You could also pass options if you need

editor.runCommand('my-command-id', { some: 'option' });
-

Then you can get the same object as a third argument of the callback.

commands.add('my-command-id', (editor, sender, options = {}) => {
+

Then you can get the same object as a third argument of the callback.

commands.add('my-command-id', (editor, sender, options = {}) => {
   alert(`This is my command ${options.some}`);
 });
 

The second argument, sender, just indicates who requested the command, in our case will be always the editor

Until now there is nothing exciting except a common entry point for functions, but we'll see later its real advantages.

Default commands

GrapesJS comes along with some default set of commands and you can get a list of all currently available commands via editor.Commands.getAll(). This will give you an object of all available commands, so, also those added later, like via plugins. You can recognize default commands by their namespace core:*, we also recommend to use namespaces in your own custom commands, but let's get a look more in detail here:

Stateful commands

As we've already seen the command is just a function and once executed nothing is left behind, but in some cases, we'd like to keep a track of executed commands. GrapesJS can handle by default this case and to enable it you just need to declare a command as an object with the run and stop methods

commands.add('my-command-state', {
-  run(editor) {
+  run(editor) {
     alert('This command is now active');
   },
-  stop(editor) {
+  stop(editor) {
     alert('This command is disabled');
   },
 });
 

So if we now run editor.runCommand('my-command-state') the command will be registered as active. To check the state of the command you can use commands.isActive('my-command-state') or you can even get the list of all active commands via commands.getActive(), in our case the result would be something like this

{
   ...
-  'my-command-state': undefined
+  'my-command-state': undefined
 }
 

The key of the result object tells you the active command, the value is the last return of the run command, in our case is undefined because we didn't return anything, but it's up to your implementation decide what to return and if you actually need it.

// Let's return something
 ...
-run(editor) {
+run(editor) {
     alert('This command is now active');
     return {
       activated: new Date(),
@@ -84,18 +84,18 @@ commands.add
 ...
 // Now instead of the `undefined` you'll see the object from the run method
 

To disable the command use editor.stopCommand method, so in our case it'll be editor.stopCommand('my-command-state'). As for the runCommand you can pass an options object as a second argument and use them in your stop method.

Once the command is active, if you try to run editor.runCommand('my-command-state') again you'll notice that that the run is not triggering. This behavior is useful to prevent executing multiple times the activation process which might lead to an inconsistent state (think about, for instance, having a counter, which should be increased on run and decreased on stop). If you need to run a command multiple times probably you're dealing with a not stateful command, so try to use it without the stop method, but in case you're aware of your application state you can actually force the execution with editor.runCommand('my-command-state', { force: true }). The same logic applies to the stopCommand method.


WARNING

If you deal with UI in your stateful commands, be careful to keep the state coherent with your logic. Let's take, for example, the use of a modal as an indicator of the command state.

commands.add('my-command-modal', {
-  run(editor) {
+  run(editor) {
     editor.Modal.open({
       title: 'Modal example',
       content: 'My content',
     });
   },
-  stop(editor) {
+  stop(editor) {
     editor.Modal.close();
   },
 });
 

If you run it, close the modal (eg. by clicking the 'x' on top) and then try to run it again you'll see that the modal is not opening anymore. This happens because the command is still active (you should see it in commands.getActive()) and to fix it you have to disable it once the modal is closed.

...
-  run(editor) {
+  run(editor) {
     editor.Modal.open({
       title: 'Modal example',
       content: 'My content',
@@ -103,10 +103,10 @@ commands.add
   },
 ...
 

In the example above, we make use of few helper methods from the Modal module (onceClose) and the command itself (stopCommand) but obviously, the logic might be different due to your requirements and specific UI.

Extending

Another big advantage of commands is the possibility to easily extend or override them with another command. -Let's take a simple example

commands.add('my-command-1', editor => {
+Let's take a simple example

commands.add('my-command-1', editor => {
   alert('This is command 1');
 });
-

If you need to overwrite this command with another one, just add it and keep the same id.

commands.add('my-command-1', editor => {
+

If you need to overwrite this command with another one, just add it and keep the same id.

commands.add('my-command-1', editor => {
   alert('This is command 1 overwritten');
 });
 

Let's see now instead how can we extend one

commands.add('my-command-2', {
@@ -142,16 +142,16 @@ editor.on.on('stop:my-command-modal:before', () => {
   console.log('Before `my-command-modal` is stopped');
 });
-

If you need, you can also listen to all commands

editor.on('run', commandId => {
+

If you need, you can also listen to all commands

editor.on('run', commandId => {
   console.log('Run', commandId);
 });
 
-editor.on('stop', commandId => {
+editor.on('stop', commandId => {
   console.log('Stop', commandId);
 });
 

Interrupt command flow

Sometimes you might need to interrupt the execution of an existant command due to some condition. In that case, you have to use run:{COMMAND-ID}:before event and set to true the abort option

const condition = 1;
 
-editor.on('run:my-command-modal:before', options => {
+editor.on('run:my-command-modal:before', options => {
   if (condition) {
     options.abort = true;
     console.log('Prevent `my-command-modal` from execution');
@@ -164,6 +164,6 @@ editor.on →
       

- + diff --git a/docs/modules/Components-js.html b/docs/modules/Components-js.html index 27775f96a..66189bd9f 100644 --- a/docs/modules/Components-js.html +++ b/docs/modules/Components-js.html @@ -8,9 +8,9 @@ - - - + + +

Now if you drag the new block inside the canvas you'll see an alert and the message in console, as you might expect. One thing worth noting is that this context is bound to the component element, so if you wanted to change a property you'd do this.innerHTML = 'inner content'.

One thing you should take in account is how the script is bound to component once rendered in the canvas or in your final template. If you check now the generated HTML coded by the editor (via Export button or editor.getHtml()), you might see something like this:

As you see the editor attaches a unique ID to all components with scripts and retrieves them via querySelectorAll. Dragging another test-block will generate this:

Keep in mind that all component scripts are executed only inside the iframe of the canvas (isolated, just like your final template), and therefore are NOT part of the current document. All your external libraries (eg. jQuery) are not there, but you'll see later how to manage scripted components with dependencies.

One thing you might be concerned about is a string used for the script, definitely not the best way to deal with a code, for this reason GrapesJS is able also to handle functions for you, so the previous example might look like this:

Unfortunately, this won't work. You'll get an undefined myVar error. The final HTML, with script functions converted to string, will look like this:

There is a solution to make your scripts behave dynamically. You can interpolate properties of the component model.

The final HTML will be:

You can even change the tags used for the interpolation

var editor = grapesjs.init({
   ...
   // Default values
@@ -129,7 +129,7 @@ editor.BlockManagerproperty Traits to create custom components.

Dependencies

As we mentioned above, scripts are executed independently inside the iframe of the canvas, without any dependencies, so exactly as the final HTML generated by the editor. If you want to make use of external libraries you have two approaches: component-related and template-related.

If you're building a slider component based on some third-party library you probably would like to include the external file only when the component is actually dragged inside the canvas. In this case, the component-related approach is the perfect one as it's loading external libraries dynamically. All you have to do is to require the dependency when it is needed and then call your script.

...
-script: function () {
+script: function () {
   var el = this;
   var initMySLider = function() {
     CoolSliderJS.init(el);
@@ -151,7 +151,7 @@ script: funct
 });
 
 ...
-  script: function () {
+  script: function () {
     // Do stuff using jquery
     $('...');
   },
@@ -163,6 +163,6 @@ script: funct
           Traits
          →
       

- + diff --git a/docs/modules/Components-new.html b/docs/modules/Components-new.html new file mode 100644 index 000000000..3f4b5622b --- /dev/null +++ b/docs/modules/Components-new.html @@ -0,0 +1,361 @@ + + + + + + Component Manager | GrapesJS + + + + + + + + + +

Component Manager

The Component is the base element for template composition. It is atomic, so elements like images, text boxes, maps, etc. fit the definition of a Component. The concept of the component was made to allow the developer to bind different behaviors to different elements. Like for example, opening the Asset Manager on double click of the image.

WARNING

This guide is referring to GrapesJS v0.14.67 or higher

How Components work?

Let's see in detail how components work by looking at all steps from adding an HTML string to the editor.

This is how we can add new components to the canvas:

// Append components directly to the canvas
+editor.addComponents(`<div>
+  <img src="https://path/image" />
+  <span title="foo">Hello world!!!</span>
+</div>`);
+
+// or into some, already defined, component.
+// For instance, appending to a selected component would be:
+editor.getSelected().append(`<div>...`);
+
+// Actually, editor.addComponents is an alias of...
+editor.getWrapper().append(`<div>...`);
+

TIP

If you need to append a component in a specific position, you can use at option. To add a component on top of all others (in the same collection) you would use

component.append('<div>...', { at: 0 })
+

or in the middle

const { length } = component.components();
+component.append('<div>...', { at: parseInt(length / 2, 10) })
+

Component Definition

In the first step the HTML string is parsed and trasformed to what is called Component Definition, so the result of the input would be:

{
+  tagName: 'div',
+  components: [
+    {
+      type: 'image',
+      attributes: { src: 'https://path/image' },
+    }, {
+      tagName: 'span',
+      type: 'text',
+      attributes: { title: 'foo' },
+      components: [{
+        type: 'textnode',
+        content: 'Hello wdsforld!!!'
+      }]
+    }
+  ]
+}
+

The real Component Definition would be a little bit bigger so we reduced the JSON for the sake of simplicity.

You can notice the result is similar to what is generally called a Virtual DOM, a lightweight rappresentation of the DOM element. This actually helps the editor to keep track of the state of our elements and make performance-friendly changes/updates. +The meaning of properties like tagName, attributes and components are quite obvious, but what about type?! This particular property specifies the actual Component of our Component Definition (you check the list of default components below) and if it's omitted, the default one will be used type: 'default'. +At this point, a good question would be, how the editor assignes those types by starting from a simple HTML string? This step is identified as Component Recognition and it's explained in detail in the next paragraph.

Component Recognition and Component Type Stack

As we said before, when you pass an HTML string as a component to the editor, that string is parsed and compiled to the Component Definition with a new type property. To understand what type should be assigned, for each parsed HTML Element, the editor iterates over all the defined components, called Component Type Stack, and checks via isComponent method (we will see it later) if that component type is appropriate for that element. The Component Type Stack is just a simple array of components but the important part is the order of those components. Any new added Custom Component (we'll see later how to create them) goes on top of the Component Type Stack and each element returned from the parser iterates the stack from top to bottom (the last element of the stack is the default one), the iteration stops once one of the component returns a truthy value from the isComponent method.

SVG - ComponentTypeStack

TIP

If you're importing big chunks of HTML code you might want to improve the performances by skipping the parsing and the component recognition steps by passing directly Component Definiton objects or using the JSX syntax. Read more about it here...TODO

Component Creation

Once the Component Definition is ready and the type is assigned, the Component instance can be created (known also as the Model). Let's step back to our previous example with the HTML string, the result of the append method is an array of added components.

const component = editor.addComponents(`<div>
+  <img src="https://path/image" />
+  <span title="foo">Hello world!!!</span>
+</div>`)[0];
+

The Component instance contains properties and methods which allows you to obtain its data and change them. +You can read properties with the get method, like, for example, the type

const componentType = component.get('type'); // eg. 'image'
+

and to update properties you'd use set, which might change the way a component behavies in the canvas.

// Make the component not draggable
+component.set('draggable', false);
+

You can also use methods like getAttributes, setAttributes, components, etc.

const innerComponents = component.components();
+// Update component content
+component.components(`<div>Component 1</div><div>Component 2</div>`);
+

Each component can define its own properties and methods but all of them will always extend, at least, the default one (then you will see how to create new custom components and how to extend the already defined) so it's good to check the Component API to see all available properties and methods.

The main purpose of the Component is to keep track of its data and to return them when necessary. One common thing you might need to ask from the component is to show its current HTML

const componentHTML = component.toHTML();
+

This will return a string containing the HTML of the component and all of its children. +The component implements also toJSON methods so you can get its JSON structure in this way

JSON.stringify(component)
+

TIP

For storing/loading all the components you should rely on the Storage Manager

The Component instance is responable for the final data (eg. HTML, JSON) of your templates, so if you need, for example, to update/add some attribute in the HTML you need to update its component (eg. component.addAttributes({ title: 'Title added' })), so the Component/Model is your Source of Truth.

Component Rendering

Another important thing of components is how they are rendered in the canvas, this aspect is handled by the View of the component. It has nothing to do with the final data, you can return a big <div>...</div> string as HTML of your component but render it as a simple image in the canvas (think about placeholders for complex/dynamic data).

So, by default, the view of components is automatically synced with the data of its models (you can't have a View without a Model). If you update the attribute of the component or append a new one as a child, the view will render it in the canvas. +Unfotunatelly, sometimes, you might need some additional logic to handle better the component result. Think about allowing a user build its <table> element, for this specific case you might want to add custom buttons in the canvas, so it'd be easier adding/removing columns/rows. To handle those cases you can rely on the View, where you can add additional DOM component, attach events, etc. All of this will be completely unrelated with the final HTML of the <table> (the result the user would expect) as it handled by the Model. +Once the component is rendered (when you actually see it in the canvas) you can always access its View and the DOM element.

const component = editor.getSelected();
+// Get the View
+const view = component.getView();
+// Get the DOM element
+const el =  component.getEl();
+

So generally, the View is something you wouldn't need to change as the default one handles already the sync with the Model but in case you'd need more control over elements (eg. custom UI in canvas) you'll probably need to create a custom component and extend the default View with your logic. We'll see later how to create custom components.

So far we have seen the core concept behind Components and how they work. The Model/Component is the source of truth for the final code of templates (eg. the HTML export relies on it) and the View/ComponentView is what is used by the editor to preview our components to users in the canvas.

TODO +A more advanced use case of custom components is an implementation of a custom renderer inside of them

Built-in Components

Here below you can see the list of built-in components, ordered by their position in the Component Type Stack

  • cell - Component for handle <td> and <th> elements
  • row - Component for handle <tr> elements
  • table - Component for handle <table> elements
  • thead - Component for handle <thead> elements
  • tbody - Component for handle <tbody> elements
  • tfoot - Component for handle <tfoot> elements
  • map - Component for handle <a> elements
  • link - Component for handle <a> elements
  • label - Component for handle properly <label> elements
  • video - Component for videos
  • image - Component for images
  • script - Component for handle <script> elements
  • svg - Component for handle SVG elements
  • comment - Component for comments (might be useful for email editors)
  • textnode - Similar to the textnode in DOM definition, so a text element without a tag element.
  • text - A simple text component that can be edited inline
  • wrapper - The canvas need to contain a root component, a wrapper, this component was made to identify it
  • default - Default base component

Define new Component

Now that we know how components work, we can start exploring the process of creating new Custom Components.

Let's say we want to make the editor understand and handle better <input> elements

First of all, place your components inside a plugin

--- OLD

Component recognition

But now, how does the editor recognize which Component to bind to the img element and what to do with the span one? +Each Component inherits, from the base one, a particular static method

/**
+ * @param {HTMLElement} el
+ * @return {Object}
+ */
+isComponent: function(el) {
+  ...
+}
+

This method gives us the possibility to recognize and bind component types to each HTMLElement (div, img, iframe, etc.). Each HTML string/element introduced inside the canvas will be processed by isComponent of all available types and if it matches, the object represented the type should be returned. The method isComponentis skipped if you add the component object ({ type: 'my-custom-type', tagName: 'div', attribute: {...}, ...}) or declare the type explicitly on the element (<div data-gjs-type="my-custom-type">...</div>)

For example, with the image component this method looks like:

// Image component
+isComponent: function(el) {
+  if(el.tagName == 'IMG')
+    return {type: 'image'};
+}
+

Let's try with something that might look a little bit tricky. What about a Google Map?!? Google Maps are generally embedded as iframes, but the template can be composed by a lot of different iframes. How can I tell the editor that a particular iframe is actually a Google's Map? Well, you'll have to figure out the right pattern, you have the HTMLElement so you can make all the checks you want. In this particular case this pattern is used:

// Map component
+isComponent: function(el) {
+	if(el.tagName == 'IFRAME' && /maps\.google\.com/.test(el.src)) {
+		return {type: 'map', src: el.src};
+	}
+},
+

In addition to tagName check, we also used the src property, but you can actually override it with your own logic by extending the built-in component.

Define new Component

Let's see an example with another HTML element that is not handled by default Component types. What about input elements?

With the default GrapesJS configuration inputs are treated like any other element; you can move it around, style it, etc. However, we'd like to handle this type of element more specifically. In this case, we have to create a new Component type.

Let's define few specs for our new Input type:

  • Can be dropped only inside form elements
  • Can't drop other elements inside it
  • Can change the type of the input (text, password, email, etc.)
  • Can make it required for the form

To define a new Component type you need to choose from which built-in Component inherit its properties, in our case we just gonna choose the default one. Let's see a complete example of the new type definition

// Get DomComponents module
+var comps = editor.DomComponents;
+
+// Get the model and the view from the default Component type
+var defaultType = comps.getType('default');
+var defaultModel = defaultType.model;
+var defaultView = defaultType.view;
+
+var inputTypes = [
+  {value: 'text', name: 'Text'},
+  {value: 'email', name: 'Email'},
+  {value: 'password', name: 'Password'},
+  {value: 'number', name: 'Number'},
+];
+
+// The `input` will be the Component type ID
+comps.addType('input', {
+  // Define the Model
+  model: defaultModel.extend({
+    // Extend default properties
+    defaults: Object.assign({}, defaultModel.prototype.defaults, {
+      // Can be dropped only inside `form` elements
+      draggable: 'form, form *',
+      // Can't drop other elements inside it
+      droppable: false,
+      // Traits (Settings)
+      traits: ['name', 'placeholder', {
+          // Change the type of the input (text, password, email, etc.)
+          type: 'select',
+          label: 'Type',
+          name: 'type',
+          options: inputTypes,
+        },{
+          // Can make it required for the form
+          type: 'checkbox',
+          label: 'Required',
+          name: 'required',
+      }],
+    }),
+  },
+  // The second argument of .extend are static methods and we'll put inside our
+  // isComponent() method. As you're putting a new Component type on top of the stack,
+  // not declaring isComponent() might probably break stuff, especially if you extend
+  // the default one.
+  {
+    isComponent: function(el) {
+      if(el.tagName == 'INPUT'){
+        return {type: 'input'};
+      }
+    },
+  }),
+
+  // Define the View
+  view: defaultType.view,
+});
+

The code above is pretty much self-explanatory and as you see a lot of work is basically done on top of the Model properties. +The View is just extending the default one, so to cover also this part let's add some random behavior.

comps.addType('input', {
+  model: {...},
+  view: defaultType.view.extend({
+    // Bind events
+    events: {
+      // If you want to bind the event to children elements
+      // 'click .someChildrenClass': 'methodName',
+      click: 'handleClick',
+      dblclick: function(){
+        alert('Hi!');
+      }
+    },
+
+    // It doesn't make too much sense this method inside the component
+    // but it's ok as an example
+    randomHex: function() {
+      return '#' + Math.floor(Math.random()*16777216).toString(16);
+    },
+
+    handleClick: function(e) {
+      this.model.set('style', {color: this.randomHex()}); // <- Affects the final HTML code
+      this.el.style.backgroundColor = this.randomHex(); // <- Doesn't affect the final HTML code
+      // Tip: updating the model will reflect the changes to the view, so, in this case,
+      // if you put the model change after the DOM one this will override the backgroundColor
+      // change made before
+    },
+
+    // The render() should return 'this'
+    render: function () {
+      // Extend the original render method
+      defaultType.view.prototype.render.apply(this, arguments);
+      this.el.placeholder = 'Text here'; // <- Doesn't affect the final HTML code
+      return this;
+    },
+  }),
+});
+

From the example above you can notice few interesting things: how to bind events, how to update directly the DOM and how to update the model. The difference between updating the DOM and the model is that the HTML code (the one you get with editor.getHtml()) is generated from the Model so updating directly the DOM will not affect it, it's just the change for the canvas.

Update Component type

Here an example of how easily you can update/override the component

var originalMap = comps.getType('map');
+
+comps.addType('map', {
+  model: originalMap.model.extend({
+    // Override how the component is rendered to HTML
+    toHTML: function() {
+      return '<div>My Custom Map</div>';
+    },
+  }, {
+    isComponent: function(el) {
+      // ... new logic for isComponent
+		},
+  }),
+  view: originalMap.view
+});
+

Improvement over addType 0.14.50+

Now, with the 0.14.50 release, defining new components or extending them is a bit easier (without breaking the old process)

  • If you don't specify the type to extend, the default one will be used. In that case, you just +use objects for model and view
  • The defaults property, in the model, will be merged automatically with defaults of the parent component
  • If you use an object in model you can specify isComponent outside or omit it. In this case, +the isComponent is not mandatory but without it means the parser won't be able to identify the component +if not explicitly declared (eg. <div data-gjs-type="new-component">...</div>)

Before

const defaultType = comps.getType('default');
+
+comps.addType('new-component', {
+  model: defaultType.model.extend({
+    defaults: {
+      ...defaultType.model.prototype.defaults,
+      someprop: 'somevalue',
+    },
+    ...
+  }, {
+    // Even if it returns false, declaring isComponent is mandatory
+    isComponent(el) {
+      return false;
+    },
+  }),
+  view: defaultType.view.extend({ ... });
+});
+

After

comps.addType('new-component', {
+  // We can even omit isComponent here, as `false` return will be the default behavior
+  isComponent: el => false,
+  model: {
+    defaults: {
+      someprop: 'somevalue',
+    },
+    ...
+  },
+  view: { ... };
+});
+
  • If you need to extend some component, you can use extend and extendView property.
  • You can now omit view property if you don't need to change it

Before

const originalMap = comps.getType('map');
+
+comps.addType('map', {
+  model: originalMap.model.extend({
+    ...
+  }, {
+    isComponent(el) {
+      // ... usually, you'd reuse the same logic
+    },
+  }),
+  // Even if I do nothing in view, I have to specify it
+  view: originalMap.view
+});
+

After

The map type is already defined, so it will be used as a base for the model and view. +We can skip isComponent if the recognition logic is the same of the extended component.

comps.addType('map', {
+  model: { ... },
+});
+

Extend the model and view with some other, already defined, components.

comps.addType('map', {
+  extend: 'other-defined-component',
+  model: { ... }, // Will extend 'other-defined-component'
+  view: { ... }, // Will extend 'other-defined-component'
+  // `isComponent` will be taken from `map`
+});
+
comps.addType('map', {
+  extend: 'other-defined-component',
+  model: { ... }, // Will extend 'other-defined-component'
+  extendView: 'other-defined-component-2',
+  view: { ... }, // Will extend 'other-defined-component-2'
+  // `isComponent` will be taken from `map`
+});
+

Extend parent functions 0.14.60+

When you need to reuse functions, of the parent you're extending, you can avoid writing something like this in any function:

domc.getType('parent-type').model.prototype.init.apply(this, arguments);
+

by using extendFn and extendFnView arrays:

domc.addType('new-type', {
+  extend: 'parent-type',
+  extendFn: ['init'], // array of model functions to extend
+  model: {
+    init() {
+      // do something;
+    },
+  }
+});
+

The same would be for the view by using extendFnView

Lifecycle Hooks

Each component triggers different lifecycle hooks, which allows you to add custom actions at their specific stages. +We can distinguish 2 different types of hooks: global and local. +You define local hooks when you create/extend a component type (usually via some model/view method) and the reason is to react to an event of that +particular component type. Instead, the global one, will be called indistinctly on any component (you listen to them via editor.on) and you can make +use of them for a more generic use case or also listen to them inside other components.

Let's see below the flow of all hooks:

  • Local hook: model.init() method, executed once the model of the component is initiliazed
  • Global hook: component:create event, called right after model.init(). The model is passed as an argument to the callback function. +Es. editor.on('component:create', model => console.log('created', model))
  • Local hook: view.init() method, executed once the view of the component is initiliazed
  • Local hook: view.onRender() method, executed once the component is rendered on the canvas
  • Global hook: component:mount event, called right after view.onRender(). The model is passed as an argument to the callback function.
  • Local hook: model.updated() method, executes when some property of the model is updated.
  • Global hook: component:update event, called after model.updated(). The model is passed as an argument to the callback function. +You can also listen to specific property change via component:update:{propertyName}
  • Local hook: model.removed() method, executed when the component is removed.
  • Global hook: component:remove event, called after model.removed(). The model is passed as an argument to the callback function.

Below you can find an example usage of all the hooks

editor.DomComponents.addType('test-component', {
+  model: {
+    defaults: {
+      testprop: 1,
+    },
+    init() {
+      console.log('Local hook: model.init');
+      this.listenTo(this, 'change:testprop', this.handlePropChange);
+      // Here we can listen global hooks with editor.on('...')
+    },
+    updated(property, value, prevValue) {
+      console.log('Local hook: model.updated',
+        'property', property, 'value', value, 'prevValue', prevValue);
+    },
+    removed() {
+      console.log('Local hook: model.removed');
+    },
+    handlePropChange() {
+      console.log('The value of testprop', this.get('testprop'));
+    }
+  },
+  view: {
+    init() {
+      console.log('Local hook: view.init');
+    },
+    onRender() {
+      console.log('Local hook: view.onRender');
+    },
+  },
+});
+
+// A block for the custom component
+editor.BlockManager.add('test-component', {
+  label: 'Test Component',
+  content: '<div data-gjs-type="test-component">Test Component</div>',
+});
+
+// Global hooks
+editor.on(`component:create`, model => console.log('Global hook: component:create', model.get('type')));
+editor.on(`component:mount`, model => console.log('Global hook: component:mount', model.get('type')));
+editor.on(`component:update:testprop`, model => console.log('Global hook: component:update:testprop', model.get('type')));
+editor.on(`component:remove`, model => console.log('Global hook: component:remove', model.get('type')));
+

Components & JS

If you want to know how to create Components with javascript attached (eg. counters, galleries, slideshows, etc.) check the dedicated page +Components & JS

Hints

<div id="gjs">
+ ...
+ <cutom-element></cutom-element>
+ ...
+</div>
+
+<script>
+ var editor = grapesjs.init({
+      container : '#gjs',
+      fromElement: true,
+  });
+
+  editor.DomComponents.addType('cutom-element-type', {...});
+</script>
+

In the example above the editor will not get the new type from the HTML because the content is already parsed and appended, so it'll get it only with new components (eg. from Blocks)

Solution 1: turn off autorender

<script>
+ var editor = grapesjs.init({
+      autorender: 0,
+      container : '#gjs',
+      fromElement: true,
+  });
+
+  editor.DomComponents.addType('cutom-element-type', {...});
+
+  // after all new types
+  editor.render();
+</script>
+

Solution 2: put all the stuff inside a plugin (Creating plugins)

Last Updated: 8/13/2019, 5:17:26 PM
+ + + diff --git a/docs/modules/Components.html b/docs/modules/Components.html index ebfa5e72c..7077f8a71 100644 --- a/docs/modules/Components.html +++ b/docs/modules/Components.html @@ -8,9 +8,9 @@ - - - + + +

For each DOM element (div, img, span, etc.) the editor will create and store an object representation. Every future change to the template will be made on top of this structure, which will then reflect on the canvas. So each object, usually called Model (or state/store), will be the source of truth for the template, but what exactly does that mean?

In more practical example, once the template is rendered on the canvas, if you try to remove one of its elements (eg. by using the browser inspector) and ask the editor to print the HTML (using editor.getHtml()) you'll see that the element will still be there. This is because the editor relies on Models and not on the DOM elements inside the canvas. This approach allows us to be extremely flexible on how we generate the final code (from the Model) and how to render it inside the canvas (from the View).

Manage Components

Component recognition

But now, how does the editor recognize which Component to bind to the img element and what to do with the span one? Each Component inherits, from the base one, a particular static method

/**
  * @param {HTMLElement} el
  * @return {Object}
  */
-isComponent: function(el) {
+isComponent: function(el) {
   ...
 }
 

This method gives us the possibility to recognize and bind component types to each HTMLElement (div, img, iframe, etc.). Each HTML string/element introduced inside the canvas will be processed by isComponent of all available types and if it matches, the object represented the type should be returned. The method isComponentis skipped if you add the component object ({ type: 'my-custom-type', tagName: 'div', attribute: {...}, ...}) or declare the type explicitly on the element (<div data-gjs-type="my-custom-type">...</div>)

For example, with the image component this method looks like:

// Image component
-isComponent: function(el) {
+isComponent: function(el) {
   if(el.tagName == 'IMG')
     return {type: 'image'};
 }
 

Let's try with something that might look a little bit tricky. What about a Google Map?!? Google Maps are generally embedded as iframes, but the template can be composed by a lot of different iframes. How can I tell the editor that a particular iframe is actually a Google's Map? Well, you'll have to figure out the right pattern, you have the HTMLElement so you can make all the checks you want. In this particular case this pattern is used:

// Map component
-isComponent: function(el) {
+isComponent: function(el) {
 	if(el.tagName == 'IFRAME' && /maps\.google\.com/.test(el.src)) {
 		return {type: 'map', src: el.src};
 	}
@@ -95,7 +95,7 @@ comps.addTyp
   // not declaring isComponent() might probably break stuff, especially if you extend
   // the default one.
   {
-    isComponent: function(el) {
+    isComponent: function(el) {
       if(el.tagName == 'INPUT'){
         return {type: 'input'};
       }
@@ -114,19 +114,19 @@ The View is just extending the default one, so to cover also this part
       // If you want to bind the event to children elements
       // 'click .someChildrenClass': 'methodName',
       click: 'handleClick',
-      dblclick: function(){
+      dblclick: function(){
         alert('Hi!');
       }
     },
 
     // It doesn't make too much sense this method inside the component
     // but it's ok as an example
-    randomHex: function() {
+    randomHex: function() {
       return '#' + Math.floor(Math.random()*16777216).toString(16);
     },
 
-    handleClick: function(e) {
-      this.model.set('style', {color: this.randomHex()}); // <- Affects the final HTML code
+    handleClick: function(e) {
+      this.model.set('style', {color: this.randomHex()}); // <- Affects the final HTML code
       this.el.style.backgroundColor = this.randomHex(); // <- Doesn't affect the final HTML code
       // Tip: updating the model will reflect the changes to the view, so, in this case,
       // if you put the model change after the DOM one this will override the backgroundColor
@@ -134,9 +134,9 @@ The View is just extending the default one, so to cover also this part
     },
 
     // The render() should return 'this'
-    render: function () {
+    render: function () {
       // Extend the original render method
-      defaultType.view.prototype.render.apply(this, arguments);
+      defaultType.view.prototype.render.apply(this, arguments);
       this.el.placeholder = 'Text here'; // <- Doesn't affect the final HTML code
       return this;
     },
@@ -147,11 +147,11 @@ The View is just extending the default one, so to cover also this part
 comps.addType('map', {
   model: originalMap.model.extend({
     // Override how the component is rendered to HTML
-    toHTML: function() {
+    toHTML: function() {
       return '<div>My Custom Map</div>';
     },
   }, {
-    isComponent: function(el) {
+    isComponent: function(el) {
       // ... new logic for isComponent
 		},
   }),
@@ -171,7 +171,7 @@ comps.addTyp
     ...
   }, {
     // Even if it returns false, declaring isComponent is mandatory
-    isComponent(el) {
+    isComponent(el) {
       return false;
     },
   }),
@@ -179,7 +179,7 @@ comps.addTyp
 });
 

After

comps.addType('new-component', {
   // We can even omit isComponent here, as `false` return will be the default behavior
-  isComponent: el => false,
+  isComponent: el => false,
   model: {
     defaults: {
       someprop: 'somevalue',
@@ -194,7 +194,7 @@ comps.addTyp
   model: originalMap.model.extend({
     ...
   }, {
-    isComponent(el) {
+    isComponent(el) {
       // ... usually, you'd reuse the same logic
     },
   }),
@@ -218,7 +218,7 @@ We can skip isComponent if the recognition logic is the same of the
   view: { ... }, // Will extend 'other-defined-component-2'
   // `isComponent` will be taken from `map`
 });
-

Extend parent functions 0.14.60+

When you need to reuse functions, of the parent you're extending, you can avoid writing something like this in any function:

domc.getType('parent-type').model.prototype.init.apply(this, arguments);
+

Extend parent functions 0.14.60+

When you need to reuse functions, of the parent you're extending, you can avoid writing something like this in any function:

domc.getType('parent-type').model.prototype.init.apply(this, arguments);
 

by using extendFn and extendFnView arrays:

domc.addType('new-type', {
   extend: 'parent-type',
   extendFn: ['init'], // array of model functions to extend
@@ -244,7 +244,7 @@ You can also listen to specific property change via component:update:{prop
       this.listenTo(this, 'change:testprop', this.handlePropChange);
       // Here we can listen global hooks with editor.on('...')
     },
-    updated(property, value, prevValue) {
+    updated(property, value, prevValue) {
       console.log('Local hook: model.updated',
         'property', property, 'value', value, 'prevValue', prevValue);
     },
@@ -252,7 +252,7 @@ You can also listen to specific property change via component:update:{prop
       console.log('Local hook: model.removed');
     },
     handlePropChange() {
-      console.log('The value of testprop', this.get('testprop'));
+      console.log('The value of testprop', this.get('testprop'));
     }
   },
   view: {
@@ -272,10 +272,10 @@ editor.BlockManager});
 
 // Global hooks
-editor.on(`component:create`, model => console.log('Global hook: component:create', model.get('type')));
-editor.on(`component:mount`, model => console.log('Global hook: component:mount', model.get('type')));
-editor.on(`component:update:testprop`, model => console.log('Global hook: component:update:testprop', model.get('type')));
-editor.on(`component:remove`, model => console.log('Global hook: component:remove', model.get('type')));
+editor.on(`component:create`, model => console.log('Global hook: component:create', model.get('type')));
+editor.on(`component:mount`, model => console.log('Global hook: component:mount', model.get('type')));
+editor.on(`component:update:testprop`, model => console.log('Global hook: component:update:testprop', model.get('type')));
+editor.on(`component:remove`, model => console.log('Global hook: component:remove', model.get('type')));
 

Components & JS

If you want to know how to create Components with javascript attached (eg. counters, galleries, slideshows, etc.) check the dedicated page Components & JS

Hints

<div id="gjs">
  ...
@@ -283,15 +283,15 @@ editor.on</div>
 
-<script>
+<script>
  var editor = grapesjs.init({
       container : '#gjs',
       fromElement: true,
   });
 
   editor.DomComponents.addType('cutom-element-type', {...});
-</script>
-

In the example above the editor will not get the new type from the HTML because the content is already parsed and appended, so it'll get it only with new components (eg. from Blocks)

Solution 1: turn off autorender

<script>
+</script>
+

In the example above the editor will not get the new type from the HTML because the content is already parsed and appended, so it'll get it only with new components (eg. from Blocks)

Solution 1: turn off autorender

<script>
  var editor = grapesjs.init({
       autorender: 0,
       container : '#gjs',
@@ -302,14 +302,14 @@ editor.on// after all new types
   editor.render();
-</script>
-

Solution 2: put all the stuff inside a plugin (Creating plugins)

Last Updated: 4/26/2019, 10:31:58 PM

Solution 2: put all the stuff inside a plugin (Creating plugins)

Last Updated: 8/13/2019, 4:52:24 PM
- + diff --git a/docs/modules/Plugins.html b/docs/modules/Plugins.html index 2820d9e70..7256acc2b 100644 --- a/docs/modules/Plugins.html +++ b/docs/modules/Plugins.html @@ -8,9 +8,9 @@ - - - + + +

Plugins

Creating plugins in GrapesJS is pretty straightforward and here you'll get how to achieve it.

Basic plugin

The most simple plugins are just functions that are run when the editor is being built.

Plugins

Creating plugins in GrapesJS is pretty straightforward and here you'll get how to achieve it.

Basic plugin

The most simple plugins are just functions that are run when the editor is being built.

  function myPlugin(editor){
       editor.BlockManager.add('my-first-block', {
         label: 'Simple block',
         content: '<div class="my-block">This is a simple block</div>',
@@ -47,7 +47,7 @@
   });
 

Named plugin

If you're distributing your plugin globally, you may want to make a named plugin. To keep thing cleaner, so you'll probably get a similar structure:

/your/path/to/grapesjs.min.js
 /your/path/to/grapesjs-plugin.js
-

Important: The order that you load files matters. GrapesJS has to be loaded before the plugin. This sets up the grapejs global variable.

So, in your grapesjs-plugin.js file:

export default grapesjs.plugins.add('my-plugin-name', (editor, options) => {
+

Important: The order that you load files matters. GrapesJS has to be loaded before the plugin. This sets up the grapejs global variable.

So, in your grapesjs-plugin.js file:

export default grapesjs.plugins.add('my-plugin-name', (editor, options) => {
   /*
   * Here you should rely on GrapesJS APIs, so check 'API Reference' for more info
   * For example, you could do something like this to add some new command:
@@ -55,19 +55,19 @@
   * editor.Commands.add(...);
   */
 })
-

The name my-plugin-name is an ID of your plugin and you'll use it to tell your editor to grab it.

Here is a complete generic example:

<script src="http://code.jquery.com/jquery-2.2.0.min.js"></script>
+

The name my-plugin-name is an ID of your plugin and you'll use it to tell your editor to grab it.

Here is a complete generic example:

<script src="http://code.jquery.com/jquery-2.2.0.min.js"></script>
 <link rel="stylesheet" href="path/to/grapes.min.css">
-<script src="path/to/grapes.min.js"></script>
-<script src="path/to/grapesjs-plugin.js"></script>
+<script src="path/to/grapes.min.js"></script>
+<script src="path/to/grapesjs-plugin.js"></script>
 
 <div id="gjs"></div>
 
-<script type="text/javascript">
+<script type="text/javascript">
   var editor = grapesjs.init({
       container : '#gjs',
       plugins: ['my-plugin-name']
   });
-</script>
+</script>
 

Plugins with options

It's also possible to pass custom parameters to plugins in to make them more flexible.

  var editor = grapesjs.init({
       container : '#gjs',
       plugins: ['my-plugin-name'],
@@ -77,7 +77,7 @@
         }
       }
   });
-

Inside you plugin you'll get those options via options argument

export default grapesjs.plugins.add('my-plugin-name', (editor, options) => {
+

Inside you plugin you'll get those options via options argument

export default grapesjs.plugins.add('my-plugin-name', (editor, options) => {
   console.log(options);
   //{ customField: 'customValue' }
 })
@@ -100,6 +100,6 @@
           Replace Rich Text Editor
          →
       

- + diff --git a/docs/modules/Storage.html b/docs/modules/Storage.html index dda2af259..7d7bbeca5 100644 --- a/docs/modules/Storage.html +++ b/docs/modules/Storage.html @@ -8,9 +8,9 @@ - - - + + +

If for any reason you need to get the data from the remote storage you can trigger the load, at any time, manually

If for any reason you need to get the data from the remote storage you can trigger the load, at any time, manually

Similarly, you have the same control over the storing. By default, the autosave is enabled and is triggered by how many changes are made to the template (change it via stepsBeforeSave option). As before, you can disable this behavior and trigger it manually when you need it

If you need to check changes which yet need to be stored you can use editor.getDirtyCount(). At any, successful, store of the editor, it resets the count.

Setup the server

Server configuration might differ for any use case so generally, it's something up to you on how to make it work, but usually, the flow is pretty straightforward. Create two endpoints, one for storing (eg. mydomain.com/store-page/123) and the other one for loading (eg. mydomain.com/load-page/123), you can also create just one and distinguish them via HTTP methods (eg. mydomain.com/page/123, via GET you load the template, with POST you store it). When you store, the editor doesn't expect any particular result but only a valid response from the server (status code 200). When you load the template, return a JSON object with the data you have (don't forget to include the id prefix if it's used)

{
@@ -115,10 +115,10 @@ editor.StorageManagerload(keys, clb, clbErr) {
+  load(keys, clb, clbErr) {
     const result = {};
 
-    keys.forEach(key => {
+    keys.forEach(key => {
       const value = SimpleStorage[key];
       if (value) {
         result[key] = value;
@@ -135,7 +135,7 @@ editor.StorageManagerstore(data, clb, clbErr) {
+  store(data, clb, clbErr) {
     for (let key in data) {
       SimpleStorage[key] = data[key];
     }
@@ -146,17 +146,17 @@ editor.StorageManager Extend storage

Among other needs, you might need to use existing storages to create more complex uses. For example, let's say we would like to mix the local and remote storages inside another one. This is how it would look like:

const sm = editor.StorageManager;
 
 sm.add('local-remote', {
-  store(data, clb, clbErr) {
-    const remote = sm.get('remote');
-    const local = sm.get('local');
+  store(data, clb, clbErr) {
+    const remote = sm.get('remote');
+    const local = sm.get('local');
     // ...
-    remote.store(data, clb, err => {
+    remote.store(data, clb, err => {
       // eg. some error on remote side, store it locally
       local.store(data, clb, clbError);
     });
   },
 
-  load(keys, clb, clbErr) {
+  load(keys, clb, clbErr) {
     // ...
   },
 });
@@ -172,15 +172,15 @@ sm.add});
 

Examples

Here you can find some of the plugins extending the Storage Manager

Events

Another way to extend storage capabilities is to make use of GrapesJS's event hooks, you can check here the list of all available events for the Storage module. Let's see some of the cases where you might want to use them:

  • Loading animation on storage requests
editor.on('storage:start', startLoading);
 editor.on('storage:end', endLoading);
-
  • Error handling
editor.on('storage:error', (err) => {
+
  • Error handling
editor.on('storage:error', (err) => {
     alert(`Error: ${err}`);
 });
-
  • Extend parameters to store
editor.on('storage:start:store', (objectToStore) => {
+
  • Extend parameters to store
editor.on('storage:start:store', (objectToStore) => {
     if (needToAddExtraParam) {
       objectToStore.customHtml = `<div>...${editor.getHtml()}...</div>`;
     }
 });
-
  • Do stuff post load
editor.on('storage:end:load', (resultObject) => {
+
  • Do stuff post load
editor.on('storage:end:load', (resultObject) => {
     if (resultObject.hasSomeKey) {
       // do stuff
     }
@@ -192,6 +192,6 @@ editor.on →
       

- + diff --git a/docs/modules/Style-manager.html b/docs/modules/Style-manager.html index 1c2810aa2..632a07095 100644 --- a/docs/modules/Style-manager.html +++ b/docs/modules/Style-manager.html @@ -8,9 +8,9 @@ - - - + + + - + diff --git a/docs/modules/Traits.html b/docs/modules/Traits.html index 6ff7a7824..41cade69f 100644 --- a/docs/modules/Traits.html +++ b/docs/modules/Traits.html @@ -8,9 +8,9 @@ - - - + + +

Trait Manager

In GrapesJS, Traits can define different parameters and behaviors of a component. The user generally will see traits as the Settings of a component. A common use of traits is to customize element attributes (eg. placeholder for inputs) and in this case the editor comes already with some built-in, easy configurable, types.

Built-in trait types

  • text
  • number
  • checkbox
  • select
  • color

Add Traits to Components

You can add traits to the component by extending them or while creating a new one. Let's see in this example how to make inputs more customizable by the editor. All components, by default, contain two traits: id and title (at the moment of writing). So, if you select an input and open the Settings panel you will see this:

In this example we are going to create a new Component. (Check here for more details about the creation of new components with a new set of traits

var editor = grapesjs.init({...});
-var domComps = editor.DomComponents;
-var dType = domComps.getType('default');
-var dModel = dType.model;
-var dView = dType.view;
-
-domComps.addType('input', {
-    model: dModel.extend({
-      defaults: Object.assign({}, dModel.prototype.defaults, {
+    

Trait Manager

In GrapesJS, Traits define different parameters and behaviors of a component. The user generally will see traits as the Settings of a component. A common use of traits is to customize element attributes (eg. placeholder for <input>) or you can also bind them to the properties of your components and react on their changes.

WARNING

This guide is referring to GrapesJS v0.15.3 or higher.

+To get a better understanding of the content in this guide we recommend reading Components first

Add Traits to Components

Generally you define traits on the definition of your new custom components (or by extending another one). Let's see in this example how to make inputs more customizable by the editor.

All components, by default, contain two traits: id and title (at the moment of writing). So, if you select an input and open the Settings panel you will see this:

We can start by creating a new custom input component in this way:

editor.DomComponents.addType('input', {
+    isComponent: el => el.tagName == 'INPUT',
+    model: {
+      defaults: {
         traits: [
-          // strings are automatically converted to text types
-          'name',
+          // Strings are automatically converted to text types
+          'name', // Same as: { type: 'text', name: 'name' }
           'placeholder',
           {
-            type: 'select',
-            label: 'Type',
-            name: 'type',
+            type: 'select', // Type of the trait
+            label: 'Type', // The label you will see in Settings
+            name: 'type', // The name of the attribute/property to use on component
             options: [
-              {value: 'text', name: 'Text'},
-              {value: 'email', name: 'Email'},
-              {value: 'password', name: 'Password'},
-              {value: 'number', name: 'Number'},
+              { id: 'text', name: 'Text'},
+              { id: 'email', name: 'Email'},
+              { id: 'password', name: 'Password'},
+              { id: 'number', name: 'Number'},
             ]
           }, {
             type: 'checkbox',
-            label: 'Required',
             name: 'required',
         }],
-      }),
-    }, {
-      isComponent: function(el) {
-        if(el.tagName == 'INPUT'){
-          return {type: 'input'};
+        // As by default, traits are binded to attributes, so to define
+        // their initial value we can use attributes
+        attributes: { type: 'text', required: true },
+      },
+    },
+});
+

Now the result will be

If you want you can also define traits dynamically via functions, which will be created on component initialization. It might be useful if you need to create traits based on some other component characteristic.

editor.DomComponents.addType('input', {
+    isComponent: el => el.tagName == 'INPUT',
+    model: {
+      defaults: {
+        traits(component) {
+          const result = [];
+
+          // Example of some logic
+          if (component.get('draggable')) {
+            result.push('name');
+          } else {
+            result.push({
+              type: 'select',
+              // ....
+            });
+          }
+
+          return result;
         }
       },
-    }),
+    },
+});
+

If you need to react to some change of the trait you can subscribe to their attribute listeners

editor.DomComponents.addType('input', {
+  model: {
+    defaults: {
+      // ...
+    },
+
+    init() {
+      this.on('change:attributes:type', this.handleTypeChange);
+    },
+
+    handleTypeChange() {
+      console.log('Input type changed to: ', this.getAttributes().type);
+    },
+  }
+})
+

As already mentioned, by default, traits modify attributes of the model, but you can also bind them to the properties by using changeProp options.

editor.DomComponents.addType('input', {
+  model: {
+    defaults: {
+      // ...
+      traits: [
+        {
+          name: 'placeholder',
+          changeProp: 1,
+        }
+        // ...
+      ],
+      // As we switched from attributes to properties the
+      // initial value should be set from the property
+      placeholder: 'Initial placeholder',
+    },
 
-    view: dView,
+    init() {
+      // Also the listener changes from `change:attributes:*` to `change:*`
+      this.on('change:placeholder', this.handlePlhChange);
+    },
+    // ...
+  }
+})
+

Built-in trait types

GrapesJS comes along with few built-in types that you can use to define your traits:

Text

Simple text input

{
+  type: 'text', // If you don't specify the type, the `text` is the default one
+  name: 'my-trait', // Required and available for all traits
+  label: 'My trait', // The label you will see near the input
+  // label: false, // If you set label to `false`, the label column will be removed
+  placeholder: 'Insert text', // Placeholder to show inside the input
+}
+

Number

Input for numbers

{
+  type: 'number',
+  // ...
+  placeholder: '0-100',
+  min: 0, // Minimum number value
+  max: 100, // Maximum number value
+  step: 5, // Number of steps
+}
+

Checkbox

Simple checkbox input

{
+  type: 'checkbox',
+  // ...
+  valueTrue: 'YES', // Value to assign when is checked, default: `true`
+  valueFalse: 'NO', // Value to assign when is unchecked, default: `false`
+}
+

Select

Select input with options

{
+  type: 'select',
+  // ...
+  options: [ // Array of options
+    { id: 'opt1', name: 'Option 1'},
+    { id: 'opt2', name: 'Option 2'},
+  ]
+}
+

Color

Color picker

{
+  type: 'color',
+  // ...
+}
+

Button

Button with a command to assign

{
+  type: 'button',
+  // ...
+  text: 'Click me',
+  full: true, // Full width button
+  command: editor => alert('Hello'),
+  // or you can just specify the Command ID
+  command: 'some-command',
+
+}
+

Updating traits at run-time

If you need to change some trait on your component you can update it wherever you want by using Component API

The trait is a simple property of the component so to get the complete list of current traits you can use this:

const component = editor.getSelected(); // Component selected in canvas
+const traits = component.get('traits');
+traits.forEach(trait => console.log(trait.props()))
+

In case you need a single one:

const component = editor.getSelected();
+console.log(component.getTrait('type').props()); // Finds by the `name` of the trait
+

If you want, for example, updating some property of the trait, do this:

// Let's update `options` of our `type` trait, defined in Input component
+const component = editor.getSelected();
+component.getTrait('type').set('options', [
+  { id: 'opt1', name: 'New option 1'},
+  { id: 'opt2', name: 'New option 2'},
+]);
+// or with multiple values
+component.getTrait('type').set({
+  label: 'My type',
+  options: [...],
 });
-

Now the result will be

Traits modify attributes of the model (which than reflected in canvas), but you can also have traits which change the property

...
-traits: [{
-    type: 'text',
-    label: 'Test',
-    name: 'model-prop-name',
-    changeProp: 1,
-}],
-...
-

In this way you're able to listen for changes and react with your own logic

editor.DomComponents.addType('input', {
-    model: dModel.extend({
-      init() {
-        this.listenTo(this, 'change:model-prop-name', this.doStuff);
-      },
+

You can also easily add new traits or remove some other by using addTrait/removeTrait

// Add new trait
+const component = editor.getSelected();
+component.addTrait({
+  name: 'type',
+  ...
+}, { at: 0 });
+// The `at` option indicates the index where to place the new trait,
+// without it, the trait will be appended at the end of the list
+
+// Remove trait
+component.removeTrait('type');
+

Define new Trait type

Generally, for most of the cases, default types are enough, but sometimes you might need something more. +In that case, you can define a totally new type of trait and bind any kind of element to it.

Create element

Let's update the default link Component with a new kind of trait. This is the default situation of traits for a simple link.

Let's just replace all of its traits with a new one, href-next, which will allow the user to select the type of href (eg. 'url', 'email', etc.)

// Update component
+editor.DomComponents.addType('link', {
+  model: {
+    defaults: {
+      traits: [
+        {
+          type: 'href-next',
+          name: 'href',
+          label: 'New href',
+        },
+      ]
+    }
+  }
+});
+

Now you'll see a simple text input because we have not yet defined our new trait type, so let's do it:

editor.TraitManager.addType('href-next', {
+  // Expects as return a simple HTML string or an HTML element
+  createInput({ trait }) {
+    // Here we can decide to use properties from the trait
+    const traitOpts = trait.get('options') || [];
+    const options = traitOpts.lenght ? traitOpts : [
+      { id: 'url', name: 'URL' },
+      { id: 'email', name: 'Email' },
+    ];
+
+    // Create a new element container and add some content
+    const el = document.createElement('div');
+    el.innerHTML = `
+      <select class="href-next__type">
+        ${options.map(opt => `<option value="${opt.id}">${opt.name}</option>`).join('')}
+      </select>
+      <div class="href-next__url-inputs">
+        <input class="href-next__url" placeholder="Insert URL"/>
+      </div>
+      <div class="href-next__email-inputs">
+        <input class="href-next__email" placeholder="Insert email"/>
+        <input class="href-next__email-subject" placeholder="Insert subject"/>
+      </div>
+    `;
 
-      doStuff() {}
-    }),
-    ...
+    // Let's make our content interactive
+    const inputsUrl = el.querySelector('.href-next__url-inputs');
+    const inputsEmail = el.querySelector('.href-next__email-inputs');
+    const inputType = el.querySelector('.href-next__type');
+    inputType.addEventListener('change', ev => {
+      switch (ev.target.value) {
+        case 'url':
+          inputsUrl.style.display = '';
+          inputsEmail.style.display = 'none';
+          break;
+        case 'email':
+          inputsUrl.style.display = 'none';
+          inputsEmail.style.display = '';
+          break;
+      }
+    });
+
+    return el;
+  },
 });
-

Define new Trait type

If built-in types are not enough (eg. something with more complex UI) you can define a new one. -Let's see this simple textarea element which updates contents of the component.

// Each new type extends the default Trait
-editor.TraitManager.addType('content', {
-  events:{
-    'keyup': 'onChange',  // trigger parent onChange method on keyup
+

From the example above we simply created our custom inputs (by giving also the possibility to use option trait property) and defined some input switch behavior on the type change. Now the result would be something like this

Update layout

Before going forward and making our trait work let's talk about the layout structure of a trait. You might have noticed that the trait is composed by the label and input columns, for this reason, GrapesJS allows you to customize both of them.

For the label customization you might use createLabel

editor.TraitManager.addType('href-next', {
+  // Expects as return a simple HTML string or an HTML element
+  createLabel({ label }) {
+    return `<div>
+      <div>Before</div>
+      ${label}
+      <div>After</div>
+    </div>`;
   },
+  // ...
+});
+

You've probably seen already that in trait definition you can setup label: false to completely remove the label column, but in case you need to force this behavior in all instances of this trait type you can use noLabel property

editor.TraitManager.addType('href-next', {
+  noLabel: true,
+  // ...
+});
+

You might also notice that by default GrapesJS applies kind of a wrapper around your inputs, generally is ok for simple inputs but probably is not what you need where you're creating a complex custom trait. To remove the default wrapper you can use the templateInput option

editor.TraitManager.addType('href-next', {
+  // Completely remove the wrapper
+  templateInput: '',
+  // Use a new one, by specifying with `data-input` attribute where to place the input container
+  templateInput: `<div class="custom-input-wrapper">
+    Before input
+    <div data-input></div>
+    After input
+  </div>`,
+  // It might also be a function, expects an HTML string as the result
+  templateInput({ trait }) {
+    return '<div ...';
+  },
+});
+

In this case, the result will be quite raw and unstyled but the point of custom trait types is to allow you to reuse your own styled inputs, probably already designed and defined (or implemented in some UI framework). +For now, let's keep the default input wrapper and continue with the integration of our custom trait.

Bind to component

At the current state, our element created in createInput is not binded to the component so nothing happens when you update inputs, so let's do it now

editor.TraitManager.addType('href-next', {
+  // ...
+
+  // Update the component based on element changes
+  // `elInput` is the result HTMLElement you get from `createInput`
+  onEvent({ elInput, component, event }) {
+    const inputType = elInput.querySelector('.href-next__type');
+    let href = '';
 
-  /**
-  * Returns the input element
-  * @return {HTMLElement}
-  */
-  getInputEl: function() {
-    if (!this.inputEl) {
-      var input = document.createElement('textarea');
-      input.value = this.target.get('content');
-      this.inputEl = input;
+    switch (inputType.value) {
+      case 'url':
+        const valUrl = elInput.querySelector('.href-next__url').value;
+        href = valUrl;
+        break;
+      case 'email':
+        const valEmail = elInput.querySelector('.href-next__email').value;
+        const valSubj = elInput.querySelector('.href-next__email-subject').value;
+        href = `mailto:${valEmail}${valSubj ? `?subject=${valSubj}` : ''}`;
+        break;
     }
-    return this.inputEl;
+
+    component.addAttributes({ href })
   },
+});
+

Now, most of the stuff should already work (you can update the trait and check the HTML in code preview). You might wonder how the editor captures the input change and how is possible to control it. +By default, the base trait wrapper applies a listener on change event and calls onEvent on any captured event (to be captured the event should be able to bubble). If you want, for example, to update the component on input event you can change the eventCapture property

editor.TraitManager.addType('href-next', {
+  eventCapture: ['input'], // you can use multiple events in the array
+  // ...
+});
+

The last thing, you might have noticed the wrong initial render of our trait, where inputs are not populated in case of already defined href attribute. This step should be done in onUpdate method

editor.TraitManager.addType('href-next', {
+  // ...
 
-  /**
-   * Triggered when the value of the model is changed
-   */
-  onValueChange: function () {
-    this.target.set('content', this.model.get('value'));
-  }
+  // Update elements on the component change
+  onUpdate({ elInput, component }) {
+    const href = component.getAttributes().href || '';
+    const inputType = elInput.querySelector('.href-next__type');
+    let type = 'url';
+
+    if (href.indexOf('mailto:') === 0) {
+      const inputEmail = elInput.querySelector('.href-next__email');
+      const inputSubject = elInput.querySelector('.href-next__email-subject');
+      const mailTo = href.replace('mailto:', '').split('?');
+      const email = mailTo[0];
+      const params = (mailTo[1] || '').split('&').reduce((acc, item) => {
+        const items = item.split('=');
+        acc[items[0]] = items[1];
+        return acc;
+      }, {});
+      type = 'email';
+
+      inputEmail.value = email || '';
+      inputSubject.value = params.subject || '';
+    } else {
+      elInput.querySelector('.href-next__url').value = href;
+    }
+
+    inputType.value = type;
+    inputType.dispatchEvent(new CustomEvent('change'));
+  },
 });
+

Now the trait will update even on component change like:

editor.getSelected().addAttributes({ href: 'mailto:new-email@test.com?subject=NewSubject' })
+

To recap what we have done so far, to create a custom trait type all you will need are 3 methods:

  • createInput - Where we define our custom HTML element
  • onEvent - How to update the component on inputs changes
  • onUpdate - How to update inputs on component changes

Result

The final result of what we have done can be seen here +

Integrate external UI components

By looking at the example above might seems like a lot of code, but at the end, it's just about a little bit of logic and the native DOM API which is not super pretty. If you use a modern UI client framework (eg. Vue, React, etc.) you could see that the integration is even easier. There is how it would be integrating a custom Vue Slider Component as a trait

editor.TraitManager.addType('slider', {
+  createInput({ trait }) {
+    const vueInst = new Vue({ render: h => h(VueSlider) }).$mount();
+    const sliderInst = vueInst.$children[0];
+    sliderInst.$on('change', ev => this.onChange(ev)); // Use onChange to trigger onEvent
+    this.sliderInst = sliderInst;
+    return vueInst.$el;
+  },
 
-// And then use it in your component
-...
-traits: [{
-    type: 'content',
-}],
-...
-
Last Updated: 12/26/2018, 7:29:34 PM

The integration with external components is possible by following these simple core points:

  1. Component rendering: new Vue({ render: ...
    +Depends on the framework, for example, in React it should be ReactDOM.render(element, ...
  2. Change propogation: sliderInst.$on('change', ev => this.onChange(ev))
    +The framework should have a mechanism to subscribe to changes and the component should expose that change
    +We've also used onChange method which comes handy when you need to trigger manually the onEvent event (you should never call directly onEvent method, but only via onChange when you need)
  3. Property getters/setters: sliderInst.getValue()/ sliderInst.setValue(value)
    +The component should allow to read and write data from the instance
Last Updated: 8/13/2019, 5:59:17 PM
- +