Canvas Spots

by artur_arseniev

HTML

<script src="https://unpkg.com/grapesjs"></script>
<script src="https://unpkg.com/grapesjs-blocks-basic"></script>
<link rel="stylesheet" href="https://unpkg.com/grapesjs/dist/css/grapes.min.css">
<div id="gjs">
</div>
<div class="canvas-spots">
  <div v-for="spot in spots" v-if="isSpotToShow(spot)" :key="spot.id" :class="{spot: 1, 'spot-hover': isHoverSpot(spot) }" :style="spot.getStyle()">
    <button v-if="isTextSelectedSpot(spot)" class="spot-text-btn" type="button" @click="onBtnAdd">
      + Add
    </button>
    <span v-if="isHoverSpot(spot)" class="spot-hover-tag">
      Name: {{ spot.component.getName() }}
    </span>
  </div>
</div>

SCSS

body, html {
  height: 100%;
  margin: 0;
}
.spot-text-btn {
  background-color: #3b97e3;
  border: none;
  color: white;
  padding: 4px 8px;
  border-radius: 3px;
  cursor: pointer;
  position: absolute;
  left: 50%;
  bottom: 0;
  translate: -50% 120%;
  pointer-events: auto;
}
.spot-hover {
  border: 2px solid #d23be3;
}
.spot-hover-tag {
  background-color: #d23be3;
  color: white;
  padding: 4px 8px;
  position: absolute;
  left: 0;
  bottom: 0;
  translate: 0% 100%;
  white-space: nowrap;
}

Babel + JSX

const editor = grapesjs.init({
  container: '#gjs',
  height: '100%',
  storageManager: false,
  components: `
  	<div style="padding: 25px; margin: 10px">Custom Canvas Spots</div>
    <div style="padding: 25px; margin: 10px">Custom Canvas Spots</div>
    <div style="padding: 25px; margin: 10px">Custom Canvas Spots</div>
  `,
  plugins: ['gjs-blocks-basic'],
  canvas: {
    customSpots: { hover: true },
  },
});

const { Canvas } = editor;

const app = new Vue({
  el: '.canvas-spots',
  data: { 
  	spots: [],
    customSpotType: 'my-text-spot',
  },
  mounted() {
  	const { customSpotType } = this;
    // Catch-all event for any spot update
    editor.on('canvas:spot', this.onCanvasSpot);

    // Add a new custom canvas spot for the last selected text component.
    editor.on('component:toggled', (component) => {
      // Remove all spots related to our custom type
      Canvas.removeSpots({ type: customSpotType });

      if (component === editor.getSelected() && component.is('text')) {
        Canvas.addSpot({ type: customSpotType, component });
      }
    });

    editor.onReady(() => {
    	// Once the editor is ready, append our custom elements to GrapesJS spots container
      Canvas.getSpotsEl().appendChild(this.$el);
    });
  },
  methods: {
    onCanvasSpot() {
      this.spots = Canvas.getSpots();
    },
    onBtnAdd() {
      const selected = editor.getSelected();
      const parent = selected.parent();
      if (parent) {
        parent.append(
          { type: 'text', components: 'New text component' },
          { at: selected.index() + 1 }
        )
      }
    },
    isTextSelectedSpot(spot) {
      return spot.type === this.customSpotType;
    },
    isHoverSpot(spot) {
      return spot.type === 'hover';
    },
    isSpotToShow(spot) {
      return this.isTextSelectedSpot(spot) || this.isHoverSpot(spot);
    },
  }
});