UNPKG

grapesjs-clot

Version:

Free and Open Source Web Builder Framework

246 lines (228 loc) 7.7 kB
import Backbone from 'backbone'; import FrameView from './FrameView'; import { bindAll, isNumber, isNull, debounce } from 'underscore'; import { createEl, removeEl } from 'utils/dom'; import Dragger from 'utils/Dragger'; export default Backbone.View.extend({ events: { 'click [data-action-remove]': 'remove', 'mousedown [data-action-move]': 'startDrag' }, initialize(opts = {}, conf = {}) { bindAll( this, 'onScroll', 'frameLoaded', 'updateOffset', 'remove', 'startDrag' ); const { model } = this; const config = { ...(opts.config || conf), frameWrapView: this }; const { canvasView, em } = config; this.cv = canvasView; this.config = config; this.em = em; this.canvas = em && em.get('Canvas'); this.ppfx = config.pStylePrefix || ''; this.frame = new FrameView({ model, config }); this.classAnim = `${this.ppfx}frame-wrapper--anim`; this.updateOffset = debounce(this.updateOffset.bind(this)); this.updateSize = debounce(this.updateSize.bind(this)); this.listenTo(model, 'loaded', this.frameLoaded); this.listenTo(model, 'change:x change:y', this.updatePos); this.listenTo(model, 'change:width change:height', this.updateSize); this.listenTo(model, 'destroy remove', this.remove); this.updatePos(); this.setupDragger(); }, setupDragger() { const { canvas, model } = this; let dragX, dragY, zoom; const toggleEffects = on => { canvas.toggleFramesEvents(on); }; this.dragger = new Dragger({ onStart: () => { const { x, y } = model.attributes; zoom = this.em.getZoomMultiplier(); dragX = x; dragY = y; toggleEffects(); }, onEnd: () => toggleEffects(1), setPosition: posOpts => { model.set({ x: dragX + posOpts.x * zoom, y: dragY + posOpts.y * zoom }); } }); }, startDrag(ev) { ev && this.dragger.start(ev); }, __clear(opts) { const { frame } = this; frame && frame.remove(opts); removeEl(this.elTools); }, remove(opts) { this.__clear(opts); Backbone.View.prototype.remove.apply(this, arguments); ['frame', 'dragger', 'cv', 'em', 'canvas', 'elTools'].forEach( i => (this[i] = 0) ); return this; }, updateOffset() { const { em, $el, frame } = this; if (!em) return; em.runDefault({ preserveSelected: 1 }); $el.removeClass(this.classAnim); frame.model._emitUpdated(); }, updatePos(md) { const { model, el } = this; const { x, y } = model.attributes; const { style } = el; this.frame.rect = 0; style.left = isNaN(x) ? x : `${x}px`; style.top = isNaN(y) ? y : `${y}px`; md && this.updateOffset(); }, updateSize() { this.updateDim(); }, /** * Update dimensions of the frame * @private */ updateDim() { const { em, el, $el, model, classAnim, frame } = this; if (!frame) return; frame.rect = 0; $el.addClass(classAnim); const { noChanges, width, height } = this.__handleSize(); // Set width and height from DOM (should be done only once) if (isNull(width) || isNull(height)) { model.set( { ...(!width ? { width: el.offsetWidth } : {}), ...(!height ? { height: el.offsetHeight } : {}) }, { silent: 1 } ); } // Prevent fixed highlighting box which appears when on // component hover during the animation em.stopDefault({ preserveSelected: 1 }); noChanges ? this.updateOffset() : setTimeout(this.updateOffset, 350); }, onScroll() { const { frame, em } = this; em.trigger('frame:scroll', { frame, body: frame.getBody(), target: frame.getWindow() }); }, frameLoaded() { const { frame } = this; frame.getWindow().onscroll = this.onScroll; this.updateDim(); }, __handleSize() { const un = 'px'; const { model, el } = this; const { style } = el; const { width, height } = model.attributes; const currW = style.width || ''; const currH = style.height || ''; const newW = width || ''; const newH = height || ''; const noChanges = currW == newW && currH == newH; style.width = isNumber(newW) ? `${newW}${un}` : newW; style.height = isNumber(newH) ? `${newH}${un}` : newH; return { noChanges, width, height, newW, newH }; }, render() { const { frame, $el, ppfx, cv, model, el } = this; const { onRender } = model.attributes; this.__clear(); this.__handleSize(); frame.render(); $el .empty() .attr({ class: `${ppfx}frame-wrapper` }) .append( ` <div class="${ppfx}frame-wrapper__top gjs-two-color" data-frame-top> <div class="${ppfx}frame-wrapper__name" data-action-move> ${model.get('name') || ''} </div> <div class="${ppfx}frame-wrapper__top-r"> <div class="${ppfx}frame-wrapper__icon" data-action-remove style="display: none"> <svg viewBox="0 0 24 24"><path d="M19 4h-3.5l-1-1h-5l-1 1H5v2h14M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12z"></path></svg> </div> </div> </div> <div class="${ppfx}frame-wrapper__right" data-frame-right></div> <div class="${ppfx}frame-wrapper__left" data-frame-left></div> <div class="${ppfx}frame-wrapper__bottom" data-frame-bottom></div> ` ) .append(frame.el); const elTools = createEl( 'div', { class: `${ppfx}tools`, style: 'pointer-events:none; display: none' }, ` <div class="${ppfx}highlighter" data-hl></div> <div class="${ppfx}badge" data-badge></div> <div class="${ppfx}placeholder"> <div class="${ppfx}placeholder-int"></div> </div> <div class="${ppfx}ghost"></div> <div class="${ppfx}toolbar" style="pointer-events:all"></div> <div class="${ppfx}resizer"></div> <div class="${ppfx}offset-v" data-offset> <div class="gjs-marginName" data-offset-m> <div class="gjs-margin-v-el gjs-margin-v-top" data-offset-m-t></div> <div class="gjs-margin-v-el gjs-margin-v-bottom" data-offset-m-b></div> <div class="gjs-margin-v-el gjs-margin-v-left" data-offset-m-l></div> <div class="gjs-margin-v-el gjs-margin-v-right" data-offset-m-r></div> </div> <div class="gjs-paddingName" data-offset-m> <div class="gjs-padding-v-el gjs-padding-v-top" data-offset-p-t></div> <div class="gjs-padding-v-el gjs-padding-v-bottom" data-offset-p-b></div> <div class="gjs-padding-v-el gjs-padding-v-left" data-offset-p-l></div> <div class="gjs-padding-v-el gjs-padding-v-right" data-offset-p-r></div> </div> </div> <div class="${ppfx}offset-fixed-v"></div> ` ); this.elTools = elTools; const twrp = cv.toolsWrapper; twrp && twrp.appendChild(elTools); // TODO remove on frame remove onRender && onRender({ el, elTop: el.querySelector('[data-frame-top]'), elRight: el.querySelector('[data-frame-right]'), elBottom: el.querySelector('[data-frame-bottom]'), elLeft: el.querySelector('[data-frame-left]'), frame: model, frameWrapperView: this, remove: this.remove, startDrag: this.startDrag }); return this; } });