Browse Source

Merge multi-frame

pull/1800/head
Artur Arseniev 7 years ago
parent
commit
dd9aaf10df
  1. 2
      dist/css/grapes.min.css
  2. 56
      src/canvas/index.js
  3. 16
      src/canvas/model/Canvas.js
  4. 145
      src/canvas/view/CanvasView.js
  5. 33
      src/commands/index.js
  6. 77
      src/commands/view/CanvasMove.js
  7. 34
      src/commands/view/ImageComponent.js
  8. 76
      src/commands/view/InsertCustom.js
  9. 26
      src/commands/view/SelectComponent.js
  10. 7
      src/commands/view/SelectPosition.js
  11. 45
      src/commands/view/ShowOffset.js
  12. 27
      src/commands/view/TextComponent.js
  13. 4
      src/editor/model/Editor.js
  14. 29
      src/styles/scss/_gjs_canvas.scss
  15. 18
      src/styles/scss/_gjs_status.scss
  16. 2
      src/styles/scss/main.scss
  17. 407
      src/utils/Dragger.js
  18. 27
      src/utils/Sorter.js
  19. 10
      src/utils/mixins.js

2
dist/css/grapes.min.css

File diff suppressed because one or more lines are too long

56
src/canvas/index.js

@ -62,13 +62,14 @@ module.exports = () => {
* @param {Object} config Configurations
* @private
*/
init(config) {
c = config || {};
for (var name in defaults) {
if (!(name in c)) c[name] = defaults[name];
}
init(config = {}) {
c = {
...defaults,
...config
};
var ppfx = c.pStylePrefix;
this.em = c.em;
const ppfx = c.pStylePrefix;
if (ppfx) c.stylePrefix = ppfx + c.stylePrefix;
canvas = new Canvas(config);
@ -289,6 +290,16 @@ module.exports = () => {
return CanvasView.getElementPos(el, opts);
},
/**
* Returns element's offsets like margins and paddings
* @param {HTMLElement} el
* @return {Object}
* @private
*/
getElementOffsets(el) {
return CanvasView.getElementOffsets(el);
},
/**
* This method comes handy when you need to attach something like toolbars
* to elements inside the canvas, dealing with all relative position,
@ -379,22 +390,17 @@ module.exports = () => {
/**
* X and Y mouse position relative to the canvas
* @param {Event} e
* @param {Event} ev
* @return {Object}
* @private
*/
getMouseRelativeCanvas(e, options) {
var opts = options || {};
var frame = this.getFrameEl();
var body = this.getBody();
var addTop = frame.offsetTop || 0;
var addLeft = frame.offsetLeft || 0;
var yOffset = body.scrollTop || 0;
var xOffset = body.scrollLeft || 0;
getMouseRelativeCanvas(ev) {
const zoom = this.em.getZoomDecimal();
const { top, left } = CanvasView.getPosition();
return {
y: e.clientY + addTop + yOffset,
x: e.clientX + addLeft + xOffset
y: ev.clientY * zoom + top,
x: ev.clientX * zoom + left
};
},
@ -463,7 +469,7 @@ module.exports = () => {
updateClientY(ev) {
ev.preventDefault();
this.lastClientY = getPointerEvent(ev).clientY;
this.lastClientY = getPointerEvent(ev).clientY * this.em.getZoomDecimal();
},
/**
@ -503,13 +509,25 @@ module.exports = () => {
},
getScrollListeners() {
return [this.getFrameEl().contentWindow, this.getElement()];
return [this.getFrameEl().contentWindow];
},
postRender() {
if (hasDnd(c.em)) this.droppable = new Droppable(c.em);
},
setZoom(value) {
return canvas.set('zoom', parseFloat(value));
},
getZoom() {
return parseFloat(canvas.get('zoom'));
},
getZoomDecimal() {
return this.getZoom() / 100;
},
/**
* Returns wrapper element
* @return {HTMLElement}

16
src/canvas/model/Canvas.js

@ -5,11 +5,19 @@ module.exports = Backbone.Model.extend({
defaults: {
frame: '',
wrapper: '',
rulers: false
rulers: false,
zoom: 100,
x: 0,
y: 0
},
initialize(config) {
var conf = this.conf || {};
this.set('frame', new Frame(conf.frame));
initialize() {
this.set('frame', new Frame());
this.listenTo(this, 'change:zoom', this.onZoomChange);
},
onZoomChange() {
const zoom = this.get('zoom');
zoom < 1 && this.set('zoom', 1);
}
});

145
src/canvas/view/CanvasView.js

@ -1,23 +1,97 @@
import Backbone from 'backbone';
import { on, off, getElement } from 'utils/mixins';
import { on, off, getElement, getKeyChar } from 'utils/mixins';
const FrameView = require('./FrameView');
const $ = Backbone.$;
let timerZoom;
module.exports = Backbone.View.extend({
events: {
wheel: 'onWheel'
},
template() {
const { pfx } = this;
return `
<div class="${pfx}canvas__frames" data-frames></div>
<div id="${pfx}tools" class="${pfx}canvas__tools" data-tools></div>
`;
},
initialize(o) {
_.bindAll(this, 'renderBody', 'onFrameScroll', 'clearOff');
_.bindAll(this, 'renderBody', 'onFrameScroll', 'clearOff', 'onKeyPress');
on(window, 'scroll resize', this.clearOff);
const { model } = this;
this.config = o.config || {};
this.em = this.config.em || {};
this.pfx = this.config.stylePrefix || '';
this.ppfx = this.config.pStylePrefix || '';
this.className = this.config.stylePrefix + 'canvas';
this.listenTo(this.em, 'change:canvasOffset', this.clearOff);
this.listenTo(model, 'change:zoom change:x change:y', this.updateFrames);
this.toggleListeners(1);
this.frame = new FrameView({
model: this.model.get('frame'),
config: this.config
});
},
remove() {
Backbone.View.prototype.remove.apply(this, arguments);
this.toggleListeners();
},
preventDefault(ev) {
if (ev) {
ev.preventDefault();
ev._parentEvent && ev._parentEvent.preventDefault();
}
},
toggleListeners(enable) {
const method = enable ? 'on' : 'off';
const methods = { on, off };
methods[method](document, 'keypress', this.onKeyPress);
},
onKeyPress(ev) {
const { em } = this;
const key = getKeyChar(ev);
if (key === ' ' && em.getZoomDecimal() !== 1) {
this.preventDefault(ev);
em.get('Editor').runCommand('core:canvas-move');
}
},
onWheel(ev) {
if (ev.ctrlKey || ev.metaKey) {
this.preventDefault(ev);
const { model } = this;
const delta = Math.max(-1, Math.min(1, ev.wheelDelta || -ev.detail));
const zoom = model.get('zoom');
model.set('zoom', zoom + delta * 2);
}
},
updateFrames() {
const { em, model } = this;
const { x, y } = model.attributes;
const zoom = this.getZoom();
const defOpts = { preserveSelected: 1 };
const mpl = zoom ? 1 / zoom : 1;
this.framesArea.style.transform = `scale(${zoom}) translate(${x *
mpl}px, ${y * mpl}px)`;
this.clearOff();
this.onFrameScroll();
em.stopDefault(defOpts);
timerZoom && clearTimeout(timerZoom);
timerZoom = setTimeout(() => em.runDefault(defOpts));
},
getZoom() {
return this.em.getZoomDecimal();
},
/**
* Checks if the element is visible in the canvas's viewport
* @param {HTMLElement} el
@ -43,8 +117,9 @@ module.exports = Backbone.View.extend({
onFrameScroll() {
var u = 'px';
var body = this.frame.el.contentDocument.body;
this.toolsEl.style.top = '-' + body.scrollTop + u;
this.toolsEl.style.left = '-' + body.scrollLeft + u;
const zoom = this.getZoom();
this.toolsEl.style.top = '-' + body.scrollTop * zoom + u;
this.toolsEl.style.left = '-' + body.scrollLeft * zoom + u;
this.em.trigger('canvasScroll');
},
@ -188,6 +263,7 @@ module.exports = Backbone.View.extend({
oEvent.initEvent(e.type, true, true);
}
oEvent.keyCodeVal = e.keyCode;
oEvent._parentEvent = e;
['keyCode', 'which'].forEach(prop => {
Object.defineProperty(oEvent, prop, {
get() {
@ -199,12 +275,12 @@ module.exports = Backbone.View.extend({
};
[
{ event: 'keydown keyup', class: 'KeyboardEvent' }
//{ event: 'mousedown mousemove mouseup', class: 'MouseEvent' },
{ event: 'keydown keyup keypress', class: 'KeyboardEvent' },
{ event: 'wheel', class: 'WheelEvent' }
].forEach(obj =>
obj.event.split(' ').forEach(event => {
fdoc.addEventListener(event, e =>
doc.dispatchEvent(createCustomEvent(e, obj.class))
this.el.dispatchEvent(createCustomEvent(e, obj.class))
);
})
);
@ -263,6 +339,7 @@ module.exports = Backbone.View.extend({
* @private
*/
getElementPos(el, opts) {
const zoom = this.getZoom();
var opt = opts || {};
var frmOff = this.getFrameOffset();
var cvsOff = this.getCanvasOffset();
@ -271,15 +348,39 @@ module.exports = Backbone.View.extend({
var frmTop = opt.avoidFrameOffset ? 0 : frmOff.top;
var frmLeft = opt.avoidFrameOffset ? 0 : frmOff.left;
const top = eo.top + frmTop - cvsOff.top;
const left = eo.left + frmLeft - cvsOff.left;
// clientHeight/clientWidth are for SVGs
const height = el.offsetHeight || el.clientHeight;
const width = el.offsetWidth || el.clientWidth;
const top = eo.top * zoom + frmTop - cvsOff.top;
const left = eo.left * zoom + frmLeft - cvsOff.left;
const height = eo.height * zoom;
const width = eo.width * zoom;
return { top, left, height, width };
},
/**
* Returns element's offsets like margins and paddings
* @param {HTMLElement} el
* @return {Object}
* @private
*/
getElementOffsets(el) {
const result = {};
const styles = window.getComputedStyle(el);
[
'marginTop',
'marginRight',
'marginBottom',
'marginLeft',
'paddingTop',
'paddingRight',
'paddingBottom',
'paddingLeft'
].forEach(offset => {
result[offset] = parseFloat(styles[offset]) * this.getZoom();
});
return result;
},
/**
* Returns position data of the canvas element
* @return {Object} obj Position object
@ -289,11 +390,13 @@ module.exports = Backbone.View.extend({
const doc = this.frame.el.contentDocument;
if (!doc) return;
const bEl = doc.body;
const zoom = this.getZoom();
const fo = this.getFrameOffset();
const co = this.getCanvasOffset();
return {
top: fo.top + bEl.scrollTop - co.top,
left: fo.left + bEl.scrollLeft - co.left
top: fo.top + bEl.scrollTop * zoom - co.top,
left: fo.left + bEl.scrollLeft * zoom - co.left
};
},
@ -340,11 +443,15 @@ module.exports = Backbone.View.extend({
},
render() {
this.wrapper = this.model.get('wrapper');
const { el, $el, ppfx, model } = this;
this.wrapper = model.get('wrapper');
$el.html(this.template());
const $frames = $el.find('[data-frames]');
this.framesArea = $frames.get(0);
if (this.wrapper && typeof this.wrapper.render == 'function') {
this.model.get('frame').set('wrapper', this.wrapper);
this.$el.append(this.frame.render().el);
model.get('frame').set('wrapper', this.wrapper);
$frames.append(this.frame.render().el);
var frame = this.frame;
if (this.config.scripts.length === 0) {
frame.el.onload = this.renderBody;
@ -352,8 +459,7 @@ module.exports = Backbone.View.extend({
this.renderScripts(); // will call renderBody later
}
}
var ppfx = this.ppfx;
this.$el.append(`
$el.find('[data-tools]').append(`
<div id="${ppfx}tools" style="pointer-events:none">
<div class="${ppfx}highlighter"></div>
<div class="${ppfx}badge"></div>
@ -367,7 +473,6 @@ module.exports = Backbone.View.extend({
<div class="${ppfx}offset-fixed-v"></div>
</div>
`);
const el = this.el;
const toolsEl = el.querySelector(`#${ppfx}tools`);
this.hlEl = el.querySelector(`.${ppfx}highlighter`);
this.badgeEl = el.querySelector(`.${ppfx}badge`);

33
src/commands/index.js

@ -26,7 +26,7 @@
* @module Commands
*/
import { isFunction, isUndefined } from 'underscore';
import { isFunction } from 'underscore';
import CommandAbstract from './view/CommandAbstract';
module.exports = () => {
@ -81,10 +81,7 @@ module.exports = () => {
defaultCommands['select-comp'] = require('./view/SelectComponent');
defaultCommands['create-comp'] = require('./view/CreateComponent');
defaultCommands['delete-comp'] = require('./view/DeleteComponent');
defaultCommands['image-comp'] = require('./view/ImageComponent');
defaultCommands['move-comp'] = require('./view/MoveComponent');
defaultCommands['text-comp'] = require('./view/TextComponent');
defaultCommands['insert-custom'] = require('./view/InsertCustom');
defaultCommands['export-template'] = ViewCode;
defaultCommands['sw-visibility'] = require('./view/SwitchVisibility');
defaultCommands['open-layers'] = require('./view/OpenLayers');
@ -189,6 +186,7 @@ module.exports = () => {
[
['copy', 'CopyComponent'],
['paste', 'PasteComponent'],
['canvas-move', 'CanvasMove'],
['component-next', 'ComponentNext'],
['component-prev', 'ComponentPrev'],
['component-enter', 'ComponentEnter'],
@ -198,9 +196,7 @@ module.exports = () => {
['component-style-clear', 'ComponentStyleClear']
].forEach(
item =>
(defaultCommands[`core:${item[0]}`] = require(`./view/${
item[1]
}`).run)
(defaultCommands[`core:${item[0]}`] = require(`./view/${item[1]}`))
);
if (c.em) c.model = c.em.get('Canvas');
@ -239,11 +235,13 @@ module.exports = () => {
* myCommand.run();
* */
get(id) {
var el = commands[id];
let el = commands[id];
if (typeof el == 'function') {
if (isFunction(el)) {
el = new el(c);
commands[id] = el;
} else if (!el) {
em.logWarning(`'${id}' command not found`);
}
return el;
@ -346,10 +344,12 @@ module.exports = () => {
if (command && command.run) {
const id = command.id;
const editor = em.get('Editor');
result = command.callRun(editor, options);
if (id && command.stop && !command.noStop) {
active[id] = result;
if (!this.isActive(id)) {
if (id && command.stop && !command.noStop) {
active[id] = result;
}
result = command.callRun(editor, options);
}
}
@ -357,7 +357,7 @@ module.exports = () => {
},
/**
* [runCommand description]
* Stop the command
* @param {Object} command
* @param {Object} options
* @return {*} Result of the command
@ -369,8 +369,11 @@ module.exports = () => {
if (command && command.run) {
const id = command.id;
const editor = em.get('Editor');
result = command.callStop(editor, options);
if (id) delete active[id];
if (this.isActive(id)) {
if (id) delete active[id];
result = command.callStop(editor, options);
}
}
return result;

77
src/commands/view/CanvasMove.js

@ -0,0 +1,77 @@
import { bindAll } from 'underscore';
import { on, off, getKeyChar } from 'utils/mixins';
import Dragger from 'utils/Dragger';
module.exports = {
run(ed) {
bindAll(this, 'onKeyUp', 'enableDragger', 'disableDragger');
this.editor = ed;
this.canvasModel = this.canvas.getCanvasView().model;
this.toggleMove(1);
},
stop(ed) {
this.toggleMove();
this.disableDragger();
},
onKeyUp(ev) {
if (getKeyChar(ev) === ' ') {
this.editor.stopCommand(this.id);
}
},
enableDragger(ev) {
this.toggleDragger(1, ev);
},
disableDragger(ev) {
this.toggleDragger(0, ev);
},
toggleDragger(enable, ev) {
const { canvasModel, em } = this;
let { dragger } = this;
const methodCls = enable ? 'add' : 'remove';
this.getCanvas().classList[methodCls](`${this.ppfx}is__grabbing`);
if (!dragger) {
dragger = Dragger.init({
getPosition() {
return {
x: canvasModel.get('x'),
y: canvasModel.get('y')
};
},
setPosition({ x, y }) {
canvasModel.set({ x, y });
},
onStart(ev, dragger) {
em.trigger('canvas:move:start', dragger);
},
onDrag(ev, dragger) {
em.trigger('canvas:move', dragger);
},
onEnd(ev, dragger) {
em.trigger('canvas:move:end', dragger);
}
});
this.dragger = dragger;
}
enable ? dragger.start(ev) : dragger.stop();
},
toggleMove(enable) {
const { ppfx } = this;
const methodCls = enable ? 'add' : 'remove';
const methodEv = enable ? 'on' : 'off';
const methodsEv = { on, off };
const canvas = this.getCanvas();
const classes = [`${ppfx}is__grab`];
!enable && classes.push(`${ppfx}is__grabbing`);
classes.forEach(cls => canvas.classList[methodCls](cls));
methodsEv[methodEv](document, 'keyup', this.onKeyUp);
methodsEv[methodEv](canvas, 'mousedown', this.enableDragger);
methodsEv[methodEv](document, 'mouseup', this.disableDragger);
}
};

34
src/commands/view/ImageComponent.js

@ -1,34 +0,0 @@
import _ from 'underscore';
import Backbone from 'backbone';
var InsertCustom = require('./InsertCustom');
module.exports = _.extend({}, InsertCustom, {
/**
* Trigger before insert
* @param {Object} object
* @private
*
* */
beforeInsert(object) {
object.type = 'image';
object.style = {};
object.attributes = {};
object.attributes.onmousedown = 'return false';
if (
this.config.firstCentered &&
this.getCanvasWrapper() == this.sorter.target
) {
object.style.margin = '0 auto';
}
},
/**
* Trigger after insert
* @param {Object} model Model created after insert
* @private
* */
afterInsert(model) {
model.trigger('dblclick');
if (this.sender) this.sender.set('active', false);
}
});

76
src/commands/view/InsertCustom.js

@ -1,76 +0,0 @@
import _ from 'underscore';
import Backbone from 'backbone';
var CreateComponent = require('./CreateComponent');
module.exports = _.extend({}, CreateComponent, {
init(...args) {
CreateComponent.init.apply(this, args);
_.bindAll(this, 'insertComponent');
this.allowDraw = 0;
},
/**
* Run method
* @private
* */
run(em, sender, options) {
this.em = em;
this.sender = sender;
this.opt = options || {};
this.$wr = this.$wrapper;
this.enable();
},
enable(...args) {
CreateComponent.enable.apply(this, args);
this.$wr.on('click', this.insertComponent);
},
/**
* Start insert event
* @private
* */
insertComponent() {
this.$wr.off('click', this.insertComponent);
this.stopSelectPosition();
var object = this.buildContent();
this.beforeInsert(object);
var index = this.sorter.lastPos.index;
// By default, collections do not trigger add event, so silent is used
var model = this.create(this.sorter.target, object, index, null, {
silent: false
});
if (this.opt.terminateAfterInsert && this.sender)
this.sender.set('active', false);
else this.enable();
if (!model) return;
this.afterInsert(model, this);
},
/**
* Trigger before insert
* @param {Object} obj
* @private
* */
beforeInsert(obj) {},
/**
* Trigger after insert
* @param {Object} model Model created after insert
* @private
* */
afterInsert(model) {},
/**
* Create different object, based on content, to insert inside canvas
*
* @return {Object}
* @private
* */
buildContent() {
return this.opt.content || {};
}
});

26
src/commands/view/SelectComponent.js

@ -3,7 +3,6 @@ import { on, off, getUnitFromValue } from 'utils/mixins';
const ToolbarView = require('dom_components/view/ToolbarView');
const Toolbar = require('dom_components/model/Toolbar');
const key = require('keymaster');
const $ = require('backbone').$;
let showOffsets;
@ -44,7 +43,7 @@ module.exports = {
* @private
* */
toggleSelectComponent(enable) {
const em = this.em;
const { em } = this;
const method = enable ? 'on' : 'off';
const methods = { on, off };
const body = this.getCanvasBody();
@ -106,8 +105,8 @@ module.exports = {
* @param {Object} e
* @private
*/
onOut(e) {
e.stopPropagation();
onOut(ev) {
ev && ev.stopPropagation();
this.hideBadge();
this.hideHighlighter();
this.hideElementOffset();
@ -138,7 +137,8 @@ module.exports = {
* @param {Object} pos
*/
hideElementOffset(el, pos) {
this.editor.stopCommand('show-offset');
const { editor } = this;
editor && editor.stopCommand('show-offset');
},
/**
@ -280,13 +280,15 @@ module.exports = {
var bStyle = badge.style;
var u = 'px';
bStyle.display = 'block';
var canvasPos = canvas.getCanvasView().getPosition();
var canvasPos = this.getCanvasPosition();
if (canvasPos) {
var badgeH = badge ? badge.offsetHeight : 0;
var badgeW = badge ? badge.offsetWidth : 0;
var top =
pos.top - badgeH < canvasPos.top ? canvasPos.top : pos.top - badgeH;
var left = pos.left + badgeW < canvasPos.left ? canvasPos.left : pos.left;
const canvasTop = canvasPos.top;
const canvasLeft = canvasPos.left;
const posTop = pos.top - (badge ? badge.offsetHeight : 0);
const badgeW = badge ? badge.offsetWidth : 0;
var top = posTop < canvasTop ? canvasTop : posTop;
var left = pos.left + badgeW < canvasLeft ? canvasLeft : pos.left;
bStyle.top = top + u;
bStyle.left = left + u;
}
@ -673,7 +675,7 @@ module.exports = {
this.stopSelectComponent();
!opts.preserveSelected && em.setSelected(null);
this.clean();
this.hideBadge();
this.onOut();
this.hideFixedElementOffset();
this.canvas.getToolbarEl().style.display = 'none';

7
src/commands/view/SelectPosition.js

@ -22,7 +22,8 @@ module.exports = {
wmargin: 1,
nested: 1,
em: this.editorModel,
canvasRelative: 1
canvasRelative: 1,
scale: () => this.em.getZoomDecimal()
});
trg && this.sorter.startSort(trg);
},
@ -63,8 +64,8 @@ module.exports = {
this.cDim.length === 0
? $(this.outsideElem)
: !this.posIsLastEl && this.cDim[this.posIndex]
? $(this.cDim[this.posIndex][5]).parent()
: $(this.outsideElem);
? $(this.cDim[this.posIndex][5]).parent()
: $(this.outsideElem);
this.posTargetModel = this.posTargetEl.data('model');
this.posTargetCollection = this.posTargetEl.data('model-comp');
}

45
src/commands/view/ShowOffset.js

@ -11,11 +11,13 @@ module.exports = {
var opt = opts || {};
var state = opt.state || '';
var config = editor.getConfig();
const zoom = this.em.getZoomDecimal();
if (
!config.showOffsets ||
(!config.showOffsetsSelected && state == 'Fixed')
) {
editor.stopCommand(this.id, opts);
return;
}
@ -78,9 +80,11 @@ module.exports = {
}
var unit = 'px';
var marginLeftSt = style.marginLeft.replace(unit, '');
var marginTopSt = parseInt(style.marginTop.replace(unit, ''));
var marginBottomSt = parseInt(style.marginBottom.replace(unit, ''));
var marginLeftSt = parseFloat(style.marginLeft.replace(unit, '')) * zoom;
var marginRightSt = parseFloat(style.marginRight.replace(unit, '')) * zoom;
var marginTopSt = parseFloat(style.marginTop.replace(unit, '')) * zoom;
var marginBottomSt =
parseFloat(style.marginBottom.replace(unit, '')) * zoom;
var mtStyle = marginT.style;
var mbStyle = marginB.style;
var mlStyle = marginL.style;
@ -89,54 +93,55 @@ module.exports = {
var pbStyle = padB.style;
var plStyle = padL.style;
var prStyle = padR.style;
var posLeft = parseInt(pos.left);
var posLeft = parseFloat(pos.left);
var widthEl = parseFloat(style.width) * zoom + unit;
// Margin style
mtStyle.height = style.marginTop;
mtStyle.width = style.width;
mtStyle.top = pos.top - style.marginTop.replace(unit, '') + unit;
mtStyle.height = marginTopSt + unit;
mtStyle.width = widthEl;
mtStyle.top = pos.top - marginTopSt + unit;
mtStyle.left = posLeft + unit;
mbStyle.height = style.marginBottom;
mbStyle.width = style.width;
mbStyle.height = marginBottomSt + unit;
mbStyle.width = widthEl;
mbStyle.top = pos.top + pos.height + unit;
mbStyle.left = posLeft + unit;
var marginSideH = pos.height + marginTopSt + marginBottomSt + unit;
var marginSideT = pos.top - marginTopSt + unit;
mlStyle.height = marginSideH;
mlStyle.width = style.marginLeft;
mlStyle.width = marginLeftSt + unit;
mlStyle.top = marginSideT;
mlStyle.left = posLeft - marginLeftSt + unit;
mrStyle.height = marginSideH;
mrStyle.width = style.marginRight;
mrStyle.width = marginRightSt + unit;
mrStyle.top = marginSideT;
mrStyle.left = posLeft + pos.width + unit;
// Padding style
var padTop = parseInt(style.paddingTop.replace(unit, ''));
ptStyle.height = style.paddingTop;
ptStyle.width = style.width;
var padTop = parseFloat(style.paddingTop) * zoom;
ptStyle.height = padTop + unit;
ptStyle.width = widthEl;
ptStyle.top = pos.top + unit;
ptStyle.left = posLeft + unit;
var padBot = parseInt(style.paddingBottom.replace(unit, ''));
pbStyle.height = style.paddingBottom;
pbStyle.width = style.width;
var padBot = parseFloat(style.paddingBottom) * zoom;
pbStyle.height = padBot + unit;
pbStyle.width = widthEl;
pbStyle.top = pos.top + pos.height - padBot + unit;
pbStyle.left = posLeft + unit;
var padSideH = pos.height - padBot - padTop + unit;
var padSideT = pos.top + padTop + unit;
plStyle.height = padSideH;
plStyle.width = style.paddingLeft;
plStyle.width = parseFloat(style.paddingLeft) * zoom + unit;
plStyle.top = padSideT;
plStyle.left = pos.left + unit;
var padRight = parseInt(style.paddingRight.replace(unit, ''));
var padRight = parseFloat(style.paddingRight) * zoom;
prStyle.height = padSideH;
prStyle.width = style.paddingRight;
prStyle.width = padRight + unit;
prStyle.top = padSideT;
prStyle.left = pos.left + pos.width - padRight + unit;
},

27
src/commands/view/TextComponent.js

@ -1,27 +0,0 @@
import _ from 'underscore';
import Backbone from 'backbone';
var CreateComponent = require('./CreateComponent');
module.exports = _.extend({}, CreateComponent, {
/**
* This event is triggered at the beginning of a draw operation
* @param {Object} component Object component before creation
* @private
* */
beforeDraw(component) {
component.type = 'text';
if (!component.style) component.style = {};
component.style.padding = '10px';
},
/**
* This event is triggered at the end of a draw operation
* @param {Object} model Component model created
* @private
* */
afterDraw(model) {
if (!model || !model.set) return;
model.trigger('focus');
if (this.sender) this.sender.set('active', false);
}
});

4
src/editor/model/Editor.js

@ -608,6 +608,10 @@ module.exports = Backbone.Model.extend({
return this.get('changesCount');
},
getZoomDecimal() {
return this.get('Canvas').getZoomDecimal();
},
/**
* Destroy editor
*/

29
src/styles/scss/_gjs_canvas.scss

@ -13,6 +13,21 @@ $canvasTop: 40px;
left: 0;
top: 40px;
&#{gjs-is(grab)},
&#{gjs-is(grabbing)} {
.#{$cv-prefix}canvas__frames {
pointer-events: none;
}
}
&__frames {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.#{$app-prefix}ghost {
display: none;
pointer-events: none;
@ -49,13 +64,13 @@ $canvasTop: 40px;
}
/* This simulate body behaviour */
> div:first-child {
background-color: #fff;
position: relative;
height: 100%;
overflow: auto;
width: 100%;
}
// > div:first-child {
// background-color: #fff;
// position: relative;
// height: 100%;
// overflow: auto;
// width: 100%;
// }
}
.#{$cv-prefix}canvas * {

18
src/styles/scss/_gjs_status.scss

@ -0,0 +1,18 @@
$gjs-is-prefix: '.#{$app-prefix}is__';
@function gjs-is($name) {
@return '#{$gjs-is-prefix}#{$name}';
}
#{$gjs-is-prefix} {
&grab,
&grab * {
cursor: grab !important;
}
&grabbing,
&grabbing * {
@include user-select(none);
cursor: grabbing !important;
}
}

2
src/styles/scss/main.scss

@ -50,6 +50,8 @@
$prefix: $app-prefix;
@import "gjs_status";
.#{$prefix} {
@each $cnum, $ccol in (one, $primaryColor), (two, $secondaryColor), (three, $tertiaryColor), (four, $quaternaryColor) {
&#{$cnum} {

407
src/utils/Dragger.js

@ -1,48 +1,53 @@
import Backbone from 'backbone';
const $ = Backbone.$;
var getBoundingRect = (el, win) => {
var w = win || window;
var rect = el.getBoundingClientRect();
return {
left: rect.left + w.pageXOffset,
top: rect.top + w.pageYOffset,
width: rect.width,
height: rect.height
};
};
module.exports = {
// TODO move to opts
setKey(keys, command) {
//key(keys, command);
},
import { bindAll, isFunction } from 'underscore';
import { on, off } from 'utils/mixins';
const options = {
/**
* Return element position
* @param {HTMLElement} el
* @return {Object}
* Callback on start
* onStart(ev, dragger) {
* console.log('pointer start', dragger.startPointer, 'position start', dragger.startPosition);
* },
*/
getElementRect(el) {
var posFetcher = this.opts.posFetcher || '';
return posFetcher
? posFetcher(el, {
avoidFrameOffset: 1
})
: getBoundingRect(el);
},
onStart: null,
/**
* Callback on drag
* onDrag(ev, dragger) {
* console.log('pointer', dragger.currentPointer, 'position', dragger.position, 'delta', dragger.delta);
* },
*/
onDrag: null,
/**
* Callback on drag
* onEnd(ev, dragger) {
* console.log('pointer', dragger.currentPointer, 'position', dragger.position, 'delta', dragger.delta);
* },
*/
onEnd: null,
/**
* Indicate a callback where to pass an object with new coordinates
*/
setPosition: null,
/**
* Indicate a callback where to get initial coordinates.
* getPosition: () => {
* ...
* return { x: 10, y: 100 }
* }
*/
getPosition: null
};
module.exports = {
/**
* Init the resizer
* @param {Object} opts
*/
init(opts) {
bindAll(this, 'drag', 'stop');
this.opts = options;
this.setOptions(opts);
this.handleMouseDown = this.handleMouseDown.bind(this);
this.drag = this.drag.bind(this);
this.move = this.move.bind(this);
this.stop = this.stop.bind(this);
this.setKey('up, right, down, left', this.handleKey);
this.delta = { x: 0, y: 0 };
this.startPosition = this.getStartPosition();
return this;
},
@ -50,135 +55,105 @@ module.exports = {
* Update options
* @param {Object} options
*/
setOptions(opts) {
this.opts = opts || {};
setOptions(opts = {}) {
this.opts = {
...this.opts,
...opts
};
},
/**
* Focus dragger on the element
* @param {HTMLElement} el
*/
focus(el) {
// Avoid focusing on already focused element
if (el && el === this.el) {
return;
}
this.getDocumentEl(el);
this.blur();
this.el = el;
this.handlers = this.opts.dragHandlers || [el];
var elRect = this.getElementRect(el); //<-- TODO have wrong top:left
this.elRect = elRect;
this.startTop = elRect.top;
this.startLeft = elRect.left;
// TODO init snapper
this.getDocumentEl().on('mousedown', this.handleMouseDown);
toggleDrag(enable) {
const docs = this.getDocumentEl();
const method = enable ? 'on' : 'off';
const methods = { on, off };
methods[method](docs, 'mousemove', this.drag);
methods[method](docs, 'mouseup', this.stop);
},
/**
* Blur from the focused element
* Start dragging
* @param {Event} e
*/
blur() {
this.getDocumentEl().off('mousedown', this.handleMouseDown);
this.el = null;
start(ev) {
const { onStart } = this.opts;
this.toggleDrag(1);
this.startPointer = this.getPointerPos(ev);
this.startPosition = this.getStartPosition();
isFunction(onStart) && onStart(ev, this);
this.drag(ev);
},
/**
* Start dragging
* @param {Event} e
* Drag event
* @param {Event} event
*/
start(e) {
this.startPos = this.getMousePos(e);
var docs = this.getDocumentEl();
docs.on('mousemove', this.drag);
docs.on('mouseup', this.stop);
drag(ev) {
const { startPointer } = this;
const currentPos = this.getPointerPos(ev);
const delta = {
x: currentPos.x - startPointer.x,
y: currentPos.y - startPointer.y
};
let { lockedAxis } = this;
// Start callback
var onStart = this.opts.onStart;
if (typeof onStart === 'function') {
onStart(e, {
docs,
el: this.el,
start: this.startPos,
elRect: this.elRect
});
// Lock one axis
if (ev.shiftKey) {
lockedAxis = !lockedAxis && this.detectAxisLock(delta.x, delta.y);
} else {
lockedAxis = null;
}
this.drag(e);
},
if (lockedAxis === 'x') {
delta.x = startPointer.x;
} else if (lockedAxis === 'y') {
delta.y = startPointer.y;
}
/**
* Stop dragging
*/
stop(e) {
var docs = this.getDocumentEl();
docs.off('mousemove', this.drag);
docs.off('mouseup', this.stop);
this.lockedAxis = null;
this.lockedAxis = lockedAxis;
this.delta = delta;
this.move(delta.x, delta.y);
this.currentPointer = currentPos;
const { onDrag } = this.opts;
isFunction(onDrag) && onDrag(ev, this);
// Stop callback
var onEnd = this.opts.onEnd;
if (typeof onEnd === 'function') {
onEnd(e, {
docs,
delta: this.delta,
end: {
x: this.startLeft + this.delta.x,
y: this.startTop + this.delta.y
}
});
}
// In case the mouse button was released outside of the window
ev.which === 0 && this.stop(ev);
},
/**
* Handle mousedown to check if it's possible to drag
* @param {Event} e
* Stop dragging
*/
handleMouseDown(e) {
var el = e.target;
if (this.isHandler(el)) {
this.start(e);
}
stop(ev) {
const { delta } = this;
this.toggleDrag();
this.lockedAxis = null;
this.move(delta.x, delta.y, 1);
const { onEnd } = this.opts;
isFunction(onEnd) && onEnd(ev, this);
},
/**
* Detects if the clicked element is a valid handler
* @param {HTMLElement} el
* @return {Boolean}
* Move the element
* @param {integer} x
* @param {integer} y
*/
isHandler(el) {
var handlers = this.handlers;
for (var n in handlers) {
if (handlers[n] === el) return true;
}
move: function(x, y, end) {
const { el, opts } = this;
const pos = this.startPosition;
const { setPosition } = opts;
const xPos = pos.x + x;
const yPos = pos.y + y;
this.position = {
x: xPos,
y: yPos,
end
};
return false;
},
isFunction(setPosition) && setPosition(this.position);
/**
* Handle key press
* @param {Event} e
* @param {Object} handler
*/
handleKey(e, handler) {
switch (handler.shortcut) {
case 'up':
this.move(0, -1);
break;
case 'right':
this.move(1, 0);
break;
case 'down':
this.move(0, 1);
break;
case 'left':
this.move(-1, 0);
break;
if (el) {
el.style.left = `${xPos}px`;
el.style.top = `${yPos}px`;
}
},
@ -186,15 +161,15 @@ module.exports = {
* Returns documents
*/
getDocumentEl(el) {
var el = el || this.el;
if (!this.$doc) {
var docs = [document];
if (el) {
docs.push(el.ownerDocument);
}
this.$doc = $(docs);
el = el || this.el;
if (!this.docs) {
const docs = [document];
el && docs.push(el.ownerDocument);
this.docs = docs;
}
return this.$doc;
return this.docs;
},
/**
@ -202,126 +177,44 @@ module.exports = {
* @param {Event} event
* @return {Object}
*/
getMousePos(e) {
var mouseFetch = this.opts.mousePosFetcher;
return mouseFetch
? mouseFetch(e)
getPointerPos(ev) {
const getPos = this.opts.getPointerPosition;
return getPos
? getPos(ev)
: {
x: e.clientX,
y: e.clientY
x: ev.clientX,
y: ev.clientY
};
},
/**
* Drag event
* @param {Event} event
*/
drag(e) {
var lockedAxis = this.lockedAxis;
var currentPos = this.getMousePos(e);
var delta = {
x: currentPos.x - this.startPos.x,
y: currentPos.y - this.startPos.y
};
// Lock one axis
if (e.shiftKey) {
if (!lockedAxis) {
var relX = delta.x;
var relY = delta.y;
var absX = Math.abs(relX);
var absY = Math.abs(relY);
// Vertical or Horizontal lock
if (relY >= absX || relY <= -absX) {
lockedAxis = 'x';
} else if (relX > absY || relX < -absY) {
lockedAxis = 'y';
}
}
} else {
lockedAxis = null;
}
if (lockedAxis === 'x') {
delta.x = this.startPos.x;
}
getStartPosition() {
const { el, opts } = this;
const getPos = opts.getPosition;
let result = { x: 0, y: 0 };
if (lockedAxis === 'y') {
delta.y = this.startPos.y;
if (isFunction(getPos)) {
result = getPos();
} else if (el) {
result = {
x: parseFloat(el.style.left),
y: parseFloat(el.style.top)
};
}
this.lockedAxis = lockedAxis;
this.delta = delta;
this.move(delta.x, delta.y);
// Drag callback
const onDrag = this.opts.onDrag;
if (typeof onDrag === 'function') {
onDrag(e, {
delta,
current: {
x: this.startLeft + delta.x,
y: this.startTop + delta.y
},
lockedAxis
});
}
// In case the mouse button was released outside of the window
if (e.which === 0) {
this.stop(e);
}
return result;
},
/**
* Move the element
* @param {integer} x
* @param {integer} y
*/
move: function(x, y) {
this.moveX(x);
this.moveY(y);
},
detectAxisLock(x, y) {
const relX = x;
const relY = y;
const absX = Math.abs(relX);
const absY = Math.abs(relY);
/**
* Move in x direction
* @param {integer} x
*/
moveX(x) {
var el = this.el;
var opts = this.opts;
var xPos = this.startLeft + x;
const setX = this.opts.setX;
if (typeof setX === 'function') {
setX(xPos, {
el,
start: this.startLeft,
delta: x
});
} else {
el.style.left = xPos + 'px';
}
},
/**
* Move in y direction
* @param {integer} y
*/
moveY(y) {
var el = this.el;
var opts = this.opts;
var yPos = this.startTop + y;
const setY = this.opts.setY;
if (typeof setY === 'function') {
setY(yPos, {
el,
start: this.startTop,
delta: y
});
} else {
el.style.top = yPos + 'px';
// Vertical or Horizontal lock
if (relY >= absX || relY <= -absX) {
return 'x';
} else if (relX > absY || relX < -absY) {
return 'y';
}
}
};

27
src/utils/Sorter.js

@ -1,5 +1,5 @@
import Backbone from 'backbone';
import { isString, isFunction, isArray } from 'underscore';
import { isString, isFunction, isArray, result } from 'underscore';
import { on, off, matches, getElement } from 'utils/mixins';
const $ = Backbone.$;
@ -50,6 +50,7 @@ module.exports = Backbone.View.extend({
this.dragHelper = null;
this.canvasRelative = o.canvasRelative || 0;
this.selectOnEnd = !o.avoidSelectOnEnd;
this.scale = o.scale;
if (this.em && this.em.on) {
this.em.on('change:canvasOffset', this.udpateOffset);
@ -57,6 +58,10 @@ module.exports = Backbone.View.extend({
}
},
getScale() {
return result(this, scale) || 1;
},
getContainerEl() {
if (!this.el) {
var el = this.opt.container;
@ -702,19 +707,17 @@ module.exports = Backbone.View.extend({
* @return {Array<number>}
*/
getDim(el) {
const { em, canvasRelative } = this;
var top, left, height, width;
if (this.canvasRelative && this.em) {
var pos = this.em.get('Canvas').getElementPos(el);
var styles = window.getComputedStyle(el);
var marginTop = parseFloat(styles['marginTop']);
var marginBottom = parseFloat(styles['marginBottom']);
var marginRight = parseFloat(styles['marginRight']);
var marginLeft = parseFloat(styles['marginLeft']);
top = pos.top - marginTop;
left = pos.left - marginLeft;
height = pos.height + marginTop + marginBottom;
width = pos.width + marginLeft + marginRight;
if (canvasRelative && em) {
const canvas = em.get('Canvas');
const pos = canvas.getElementPos(el);
const elOffsets = canvas.getElementOffsets(el);
top = pos.top - elOffsets.marginTop;
left = pos.left - elOffsets.marginLeft;
height = pos.height + elOffsets.marginTop + elOffsets.marginBottom;
width = pos.width + elOffsets.marginLeft + elOffsets.marginRight;
} else {
var o = this.offset(el);
top = this.relative

10
src/utils/mixins.js

@ -129,6 +129,14 @@ const getModel = (el, $) => {
const getPointerEvent = ev =>
ev.touches && ev.touches[0] ? ev.touches[0] : ev;
/**
* Get cross-browser keycode
* @param {Event} ev
* @return {Number}
*/
const getKeyCode = ev => ev.which || ev.keyCode;
const getKeyChar = ev => String.fromCharCode(getKeyCode(ev));
export {
on,
off,
@ -137,6 +145,8 @@ export {
matches,
getModel,
camelCase,
getKeyCode,
getKeyChar,
getElement,
shallowDiff,
normalizeFloat,

Loading…
Cancel
Save