drapcode-builder
Version:
Drapcode Builder Library
222 lines (206 loc) • 6.85 kB
JavaScript
import Backbone from "backbone";
import FrameView from "./FrameView";
import { bindAll, isNumber, isNull, debounce } from "underscore";
import { createEl, motionsEv } 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.listenTo(model, "loaded", this.frameLoaded);
this.listenTo(model, "change:x change:y", this.updatePos);
this.listenTo(model, "change:width change:height", this.updateSize);
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);
},
remove() {
Backbone.View.prototype.remove.apply(this, arguments);
this.frame.remove();
return this;
},
updateOffset: debounce(function() {
const { em, $el, frame } = this;
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: debounce(function() {
this.updateDim();
}),
/**
* Update dimensions of the frame
* @private
*/
updateDim() {
const { em, el, $el, model, classAnim } = this;
const { width, height } = model.attributes;
const { style } = el;
const currW = style.width || "";
const currH = style.height || "";
const newW = width || "";
const newH = height || "";
const noChanges = currW == newW && currH == newH;
const un = "px";
this.frame.rect = 0;
$el.addClass(classAnim);
style.width = isNumber(newW) ? `${newW}${un}` : newW;
style.height = isNumber(newH) ? `${newH}${un}` : newH;
// Set width and height from DOM (should be done only once)
if (isNull(width) || isNull(height)) {
const newDims = {
...(!width ? { width: el.offsetWidth } : {}),
...(!height ? { height: el.offsetHeight } : {})
};
model.set(newDims, { silent: 1 });
}
// Prevent fixed highlighting box which appears when on
// component hover during the animation
em.stopDefault({ preserveSelected: 1 });
noChanges ? this.updateOffset() : $el.one(motionsEv, this.updateOffset);
},
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();
},
render() {
const { frame, $el, ppfx, cv, model, el } = this;
const { onRender } = model.attributes;
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);
console.log("Create div element frame");
const elTools = createEl(
"div",
{
class: `${ppfx}tools`,
style: "pointer-events:none; opacity: 0"
},
`
<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;
cv.toolsWrapper.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;
}
});