GrapesJS Textable Component

by artur_arseniev

HTML

<script src="https://unpkg.com/grapesjs"></script>
<link rel="stylesheet" href="https://unpkg.com/grapesjs/dist/css/grapes.min.css">
<script src="https://unpkg.com/grapesjs-blocks-basic"></script>
<div id="gjs">
  <div style="padding: 25px">Hello World!!!</div>
</div>

CSS

body, html {
  margin: 0;
  height: 100%;
}

Babel + JSX

const newPlugin = (editor) => {
  const newType = 'textable-cmp';
	
  // Define a component with `textable` property
  editor.Components.addType(newType, {
    model: {
      defaults: {
        tagName: 'span',
        textable: true,
        placeholder: '{{ VARIABLE-1 }}',
      },
      getInnerHTML() {
        return this.get('placeholder');
      },
    },
    // The view below it's just an example for creating a different UX
    view: {
      events: {
        'change': 'updatePlaceholder',
      },
      updatePlaceholder(ev) {
        this.model.set({ placeholder: ev.target.value });
      },
      onAttrUpdate() {
	      const viewStyle = 'padding: 0px 5px 1px; border-radius: 3px; border: 1px solid #b300fd';
        this.el.setAttribute('style', viewStyle);
      },
      onRender() {
        const { model, el } = this;
        const select = document.createElement('select');
        const options = [ 'VARIABLE-1', 'VARIABLE-2', 'VARIABLE-3' ];
        select.innerHTML = options.map(item => 
        	`<option value="{{ ${item} }}">${item}</option>`
        ).join('');
        el.innerHTML = '';
        el.appendChild(select);
        select.setAttribute('style', 'padding: 0; border: none; appearance: none;');
        select.value = model.get('placeholder');
      },
    }
  });
  
  editor.Blocks.add(newType, {
    label: 'Textable component',
    content: { type: newType },
    select: true,
  });

}

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