react-design-editor
Version:
Design Editor Tools with React.js + ant.design + fabric.js
883 lines (882 loc) • 37.9 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const animejs_1 = __importDefault(require("animejs"));
const fabric_1 = require("fabric");
const constants_1 = require("../constants");
/**
* Event Handler Class
* @author salgum1114
* @class EventHandler
*/
class EventHandler {
constructor(handler) {
/**
* Detach event on document
*
*/
this.destroy = () => {
if (this.handler.editable) {
this.handler.canvas.off({
'object:modified': this.modified,
'object:scaling': this.scaling,
'object:moving': this.moving,
'object:moved': this.moved,
'object:rotating': this.rotating,
'mouse:wheel': this.mousewheel,
'mouse:down': this.mousedown,
'mouse:move': this.mousemove,
'mouse:up': this.mouseup,
'selection:cleared': this.selection,
'selection:created': this.selection,
'selection:updated': this.selection,
});
}
else {
this.handler.canvas.off({
'mouse:down': this.mousedown,
'mouse:move': this.mousemove,
'mouse:out': this.mouseout,
'mouse:up': this.mouseup,
'mouse:wheel': this.mousewheel,
});
this.handler.getObjects().forEach(object => {
object.off('mousedown', this.handler.eventHandler.object.mousedown);
if (object.anime) {
animejs_1.default.remove(object);
}
});
}
this.handler.canvas.wrapperEl.removeEventListener('keydown', this.keydown);
this.handler.canvas.wrapperEl.removeEventListener('keyup', this.keyup);
this.handler.canvas.wrapperEl.removeEventListener('mousedown', this.onmousedown);
this.handler.canvas.wrapperEl.removeEventListener('contextmenu', this.contextmenu);
if (this.handler.keyEvent.clipboard) {
this.handler.canvas.wrapperEl.removeEventListener('paste', this.paste);
}
};
/**
* Individual object event
*
*/
this.object = {
/**
* Mouse down event on object
* @param {FabricEvent} opt
*/
mousedown: (opt) => {
const { target } = opt;
if (target && target.link && target.link.enabled) {
const { onClick } = this.handler;
if (onClick) {
onClick(this.handler.canvas, target);
}
}
},
/**
* Mouse double click event on object
* @param {FabricEvent} opt
*/
mousedblclick: (opt) => {
const { target } = opt;
if (target) {
const { onDblClick } = this.handler;
if (onDblClick) {
onDblClick(this.handler.canvas, target);
}
}
},
};
/**
* Modified event object
*
* @param {FabricEvent} opt
* @returns
*/
this.modified = (opt) => {
const { target } = opt;
if (!target) {
return;
}
if (target.type === 'circle' && target.parentId) {
return;
}
const { onModified } = this.handler;
if (onModified) {
onModified(target);
}
};
/**
* Moving event object
*
* @param {FabricEvent} opt
* @returns
*/
this.moving = (opt) => {
const { target } = opt;
if (this.handler.interactionMode === 'crop') {
this.handler.cropHandler.moving(opt);
}
else {
if (this.handler.editable && this.handler.guidelineOption.enabled) {
this.handler.guidelineHandler.movingGuidelines(target);
}
if (target.type === 'activeSelection') {
const activeSelection = target;
activeSelection.getObjects().forEach((obj) => {
const left = target.left + obj.left + target.width / 2;
const top = target.top + obj.top + target.height / 2;
if (obj.superType === 'node') {
this.handler.portHandler.setCoords({ ...obj, left, top });
}
else if (obj.superType === 'element') {
const { id } = obj;
const el = this.handler.elementHandler.findById(id);
// TODO... Element object incorrect position
this.handler.elementHandler.setPositionByOrigin(el, obj, left, top);
}
});
return;
}
if (target.superType === 'node') {
this.handler.portHandler.setCoords(target);
}
else if (target.superType === 'element') {
const { id } = target;
const el = this.handler.elementHandler.findById(id);
this.handler.elementHandler.setPosition(el, target);
}
}
};
/**
* Moved event object
*
* @param {FabricEvent} opt
*/
this.moved = (opt) => {
const { target } = opt;
this.handler.gridHandler.setCoords(target);
if (!this.handler.transactionHandler.active) {
this.handler.transactionHandler.save('moved');
}
if (target.superType === 'element') {
const { id } = target;
const el = this.handler.elementHandler.findById(id);
this.handler.elementHandler.setPosition(el, target);
}
};
/**
* Scaling event object
*
* @param {FabricEvent} opt
*/
this.scaling = (opt) => {
const { target } = opt;
if (this.handler.interactionMode === 'crop') {
this.handler.cropHandler.resize(opt);
}
// TODO...this.handler.guidelineHandler.scalingGuidelines(target);
if (target.superType === 'element') {
const { id, width, height } = target;
const el = this.handler.elementHandler.findById(id);
// update the element
this.handler.elementHandler.setScaleOrAngle(el, target);
this.handler.elementHandler.setSize(el, target);
this.handler.elementHandler.setPosition(el, target);
const video = target;
if (video.type === 'video' && video.player) {
video.player.setPlayerSize(width, height);
}
}
};
/**
* Scaled event object
*
* @param {FabricEvent} opt
*/
this.scaled = (_opt) => {
if (!this.handler.transactionHandler.active) {
this.handler.transactionHandler.save('scaled');
}
};
/**
* Rotating event object
*
* @param {FabricEvent} opt
*/
this.rotating = (opt) => {
const { target } = opt;
if (target.superType === 'element') {
const { id } = target;
const el = this.handler.elementHandler.findById(id);
// update the element
this.handler.elementHandler.setScaleOrAngle(el, target);
}
};
/**
* Rotated event object
*
* @param {FabricEvent} opt
*/
this.rotated = (_opt) => {
if (!this.handler.transactionHandler.active) {
this.handler.transactionHandler.save('rotated');
}
};
/**
* Moing object at keyboard arrow key down event
*
* @param {KeyboardEvent} e
* @returns
*/
this.arrowmoving = (e) => {
const activeObject = this.handler.canvas.getActiveObject();
if (!activeObject) {
return false;
}
if (activeObject.id === 'workarea') {
return false;
}
if (e.code === constants_1.code.ARROW_UP) {
activeObject.set('top', activeObject.top - 2);
activeObject.setCoords();
this.handler.canvas.renderAll();
return true;
}
else if (e.code === constants_1.code.ARROW_DOWN) {
activeObject.set('top', activeObject.top + 2);
activeObject.setCoords();
this.handler.canvas.renderAll();
return true;
}
else if (e.code === constants_1.code.ARROW_LEFT) {
activeObject.set('left', activeObject.left - 2);
activeObject.setCoords();
this.handler.canvas.renderAll();
return true;
}
else if (e.code === constants_1.code.ARROW_RIGHT) {
activeObject.set('left', activeObject.left + 2);
activeObject.setCoords();
this.handler.canvas.renderAll();
return true;
}
if (this.handler.onModified) {
this.handler.onModified(activeObject);
}
return true;
};
/**
* Zoom at mouse wheel event
*
* @param {FabricEvent<WheelEvent>} opt
* @returns
*/
this.mousewheel = (opt) => {
const event = opt;
const { zoomEnabled } = this.handler;
if (!zoomEnabled) {
return;
}
const delta = event.e.deltaY;
let zoomRatio = this.handler.canvas.getZoom();
if (delta > 0) {
zoomRatio -= this.handler.zoomStep;
}
else {
zoomRatio += this.handler.zoomStep;
}
this.handler.zoomHandler.zoomToPoint(new fabric_1.fabric.Point(this.handler.canvas.getWidth() / 2, this.handler.canvas.getHeight() / 2), zoomRatio);
event.e.preventDefault();
event.e.stopPropagation();
};
/**
* Mouse down event on object
*
* @param {FabricEvent<MouseEvent>} opt
* @returns
*/
this.mousedown = (opt) => {
const event = opt;
const { editable } = this.handler;
if (event.e.altKey && editable && !this.handler.interactionHandler.isDrawingMode()) {
this.handler.interactionHandler.grab();
this.panning = true;
return;
}
if (this.handler.interactionMode === 'grab') {
this.panning = true;
return;
}
const { target } = event;
if (editable) {
if (this.handler.prevTarget && this.handler.prevTarget.superType === 'link') {
this.handler.prevTarget.set({
stroke: this.handler.prevTarget.originStroke || this.handler.prevTarget.stroke,
});
}
if (target && target.type === 'fromPort') {
this.handler.linkHandler.init(target);
return;
}
if (target &&
this.handler.interactionMode === 'link' &&
(target.type === 'toPort' || target.superType === 'node')) {
let toPort;
if (target.superType === 'node') {
toPort = target.toPort;
}
else {
toPort = target;
}
this.handler.linkHandler.generate(toPort);
return;
}
this.handler.guidelineHandler.viewportTransform = this.handler.canvas.viewportTransform;
this.handler.guidelineHandler.zoom = this.handler.canvas.getZoom();
if (this.handler.interactionMode === 'selection') {
if (target && target.superType === 'link') {
target.set({
stroke: target.selectedStroke || 'green',
});
}
this.handler.prevTarget = target;
return;
}
if (this.handler.interactionMode === 'polygon') {
if (target && this.handler.pointArray.length && target.id === this.handler.pointArray[0].id) {
this.handler.drawingHandler.polygon.generate(this.handler.pointArray);
}
else {
this.handler.drawingHandler.polygon.addPoint(event);
}
}
else if (this.handler.interactionMode === 'line') {
if (this.handler.pointArray.length && this.handler.activeLine) {
this.handler.drawingHandler.line.generate(event);
}
else {
this.handler.drawingHandler.line.addPoint(event);
}
}
else if (this.handler.interactionMode === 'arrow') {
if (this.handler.pointArray.length && this.handler.activeLine) {
this.handler.drawingHandler.arrow.generate(event);
}
else {
this.handler.drawingHandler.arrow.addPoint(event);
}
}
}
};
/**
* Mouse move event on canvas
*
* @param {FabricEvent<MouseEvent>} opt
* @returns
*/
this.mousemove = (opt) => {
const event = opt;
if (this.handler.interactionMode === 'grab' && this.panning) {
this.handler.interactionHandler.moving(event.e);
this.handler.canvas.requestRenderAll();
}
if (!this.handler.editable && event.target) {
if (event.target.superType === 'element') {
return;
}
if (event.target.id !== 'workarea') {
if (event.target !== this.handler.target) {
this.handler.tooltipHandler.show(event.target);
}
}
else {
this.handler.tooltipHandler.hide(event.target);
}
}
if (this.handler.interactionMode === 'polygon') {
if (this.handler.activeLine && this.handler.activeLine.class === 'line') {
const pointer = this.handler.canvas.getPointer(event.e);
this.handler.activeLine.set({ x2: pointer.x, y2: pointer.y });
const points = this.handler.activeShape.get('points');
points[this.handler.pointArray.length] = {
x: pointer.x,
y: pointer.y,
};
this.handler.activeShape.set({
points,
});
this.handler.canvas.requestRenderAll();
}
}
else if (this.handler.interactionMode === 'line') {
if (this.handler.activeLine && this.handler.activeLine.class === 'line') {
const pointer = this.handler.canvas.getPointer(event.e);
this.handler.activeLine.set({ x2: pointer.x, y2: pointer.y });
}
this.handler.canvas.requestRenderAll();
}
else if (this.handler.interactionMode === 'arrow') {
if (this.handler.activeLine && this.handler.activeLine.class === 'line') {
const pointer = this.handler.canvas.getPointer(event.e);
this.handler.activeLine.set({ x2: pointer.x, y2: pointer.y });
}
this.handler.canvas.requestRenderAll();
}
else if (this.handler.interactionMode === 'link') {
if (this.handler.activeLine && this.handler.activeLine.class === 'line') {
const pointer = this.handler.canvas.getPointer(event.e);
this.handler.activeLine.set({ x2: pointer.x, y2: pointer.y });
}
this.handler.canvas.requestRenderAll();
}
return;
};
/**
* Mouse up event on canvas
*
* @param {FabricEvent<MouseEvent>} opt
* @returns
*/
this.mouseup = (opt) => {
const event = opt;
if (this.handler.interactionMode === 'grab') {
this.panning = false;
return;
}
const { target, e } = event;
if (this.handler.interactionMode === 'selection') {
if (target && e.shiftKey && target.superType === 'node') {
const node = target;
this.handler.canvas.discardActiveObject();
const nodes = [];
this.handler.nodeHandler.getNodePath(node, nodes);
const activeSelection = new fabric_1.fabric.ActiveSelection(nodes, {
canvas: this.handler.canvas,
...this.handler.activeSelectionOption,
});
this.handler.canvas.setActiveObject(activeSelection);
this.handler.canvas.requestRenderAll();
}
}
if (this.handler.editable && this.handler.guidelineOption.enabled) {
this.handler.guidelineHandler.verticalLines.length = 0;
this.handler.guidelineHandler.horizontalLines.length = 0;
}
this.handler.canvas.renderAll();
};
/**
* Mouse out event on canvas
*
* @param {FabricEvent<MouseEvent>} opt
*/
this.mouseout = (opt) => {
const event = opt;
if (!event.target) {
this.handler.tooltipHandler.hide();
}
};
/**
* Selection event event on canvas
*
* @param {FabricEvent} opt
*/
this.selection = (opt) => {
const { onSelect, activeSelectionOption } = this.handler;
const target = opt.target;
if (target && target.type === 'activeSelection') {
target.set({
...activeSelectionOption,
});
}
if (onSelect) {
onSelect(target);
}
};
/**
* Called resize event on canvas
*
* @param {number} nextWidth
* @param {number} nextHeight
* @returns
*/
this.resize = (nextWidth, nextHeight) => {
this.handler.canvas.setWidth(nextWidth).setHeight(nextHeight);
this.handler.canvas.setBackgroundColor(this.handler.canvasOption.backgroundColor, this.handler.canvas.renderAll.bind(this.handler.canvas));
if (!this.handler.workarea) {
return;
}
const diffWidth = nextWidth / 2 - this.handler.width / 2;
const diffHeight = nextHeight / 2 - this.handler.height / 2;
this.handler.width = nextWidth;
this.handler.height = nextHeight;
if (this.handler.workarea.layout === 'fixed') {
this.handler.canvas.centerObject(this.handler.workarea);
this.handler.workarea.setCoords();
if (this.handler.gridOption.enabled) {
return;
}
this.handler.canvas.getObjects().forEach((obj) => {
if (obj.id !== 'workarea') {
const left = obj.left + diffWidth;
const top = obj.top + diffHeight;
obj.set({
left,
top,
});
obj.setCoords();
if (obj.superType === 'element') {
const { id } = obj;
const el = this.handler.elementHandler.findById(id);
// update the element
this.handler.elementHandler.setPosition(el, obj);
}
}
});
this.handler.canvas.requestRenderAll();
return;
}
if (this.handler.workarea.layout === 'responsive') {
const { scaleX } = this.handler.workareaHandler.calculateScale();
const center = this.handler.canvas.getCenter();
const deltaPoint = new fabric_1.fabric.Point(diffWidth, diffHeight);
this.handler.canvas.relativePan(deltaPoint);
this.handler.zoomHandler.zoomToPoint(new fabric_1.fabric.Point(center.left, center.top), scaleX);
return;
}
const scaleX = nextWidth / this.handler.workarea.width;
const scaleY = nextHeight / this.handler.workarea.height;
const diffScaleX = nextWidth / (this.handler.workarea.width * this.handler.workarea.scaleX);
const diffScaleY = nextHeight / (this.handler.workarea.height * this.handler.workarea.scaleY);
this.handler.workarea.set({
scaleX,
scaleY,
});
this.handler.canvas.getObjects().forEach((obj) => {
const { id } = obj;
if (obj.id !== 'workarea') {
const left = obj.left * diffScaleX;
const top = obj.top * diffScaleY;
const newScaleX = obj.scaleX * diffScaleX;
const newScaleY = obj.scaleY * diffScaleY;
obj.set({
scaleX: newScaleX,
scaleY: newScaleY,
left,
top,
});
obj.setCoords();
if (obj.superType === 'element') {
const video = obj;
const { width, height } = obj;
const el = this.handler.elementHandler.findById(id);
this.handler.elementHandler.setSize(el, obj);
if (video.player) {
video.player.setPlayerSize(width, height);
}
this.handler.elementHandler.setPosition(el, obj);
}
}
});
this.handler.canvas.renderAll();
};
/**
* Paste event on canvas
*
* @param {ClipboardEvent} e
* @returns
*/
this.paste = async (e) => {
if (this.handler.canvas.wrapperEl !== document.activeElement) {
return false;
}
if (e.preventDefault) {
e.preventDefault();
}
if (e.stopPropagation) {
e.stopPropagation();
}
const clipboardData = e.clipboardData;
if (clipboardData.types.length) {
clipboardData.types.forEach((clipboardType) => {
if (clipboardType === 'text/plain') {
const textPlain = clipboardData.getData('text/plain');
try {
const objects = JSON.parse(textPlain);
const { gridOption: { grid = 10 }, isCut, } = this.handler;
const padding = isCut ? 0 : grid;
if (objects && Array.isArray(objects)) {
const filteredObjects = objects.filter(obj => obj !== null);
if (filteredObjects.length === 1) {
const obj = filteredObjects[0];
if (typeof obj.cloneable !== 'undefined' && !obj.cloneable) {
return;
}
obj.left = obj.properties.left + padding;
obj.top = obj.properties.top + padding;
const createdObj = this.handler.add(obj, false, true);
this.handler.canvas.setActiveObject(createdObj);
this.handler.canvas.requestRenderAll();
this.handler.onAdd?.(createdObj);
}
else {
const nodes = [];
const targets = [];
objects.forEach(obj => {
if (!obj) {
return;
}
if (obj.superType === 'link') {
obj.fromNodeId = nodes[obj.fromNodeIndex].id;
obj.toNodeId = nodes[obj.toNodeIndex].id;
}
else {
obj.left = obj.properties.left + padding;
obj.top = obj.properties.top + padding;
}
const createdObj = this.handler.add(obj, false, true);
if (obj.superType === 'node') {
nodes.push(createdObj);
}
else {
targets.push(createdObj);
}
});
const activeSelection = new fabric_1.fabric.ActiveSelection(nodes.length ? nodes : targets, {
canvas: this.handler.canvas,
...this.handler.activeSelectionOption,
});
this.handler.canvas.setActiveObject(activeSelection);
this.handler.canvas.requestRenderAll();
this.handler.onAdd?.(activeSelection);
}
if (!this.handler.transactionHandler.active) {
this.handler.transactionHandler.save('paste');
}
this.handler.isCut = false;
this.handler.copy();
}
}
catch (error) {
console.error(error);
// const item = {
// id: uuv4id(),
// type: 'textbox',
// text: textPlain,
// };
// this.handler.add(item, true);
}
}
else if (clipboardType === 'text/html') {
// Todo ...
// const textHtml = clipboardData.getData('text/html');
// console.log(textHtml);
}
else if (clipboardType === 'Files') {
// Array.from(clipboardData.files).forEach((file) => {
// const { type } = file;
// if (type === 'image/png' || type === 'image/jpeg' || type === 'image/jpg') {
// const item = {
// id: uuid(),
// type: 'image',
// file,
// superType: 'image',
// };
// this.handler.add(item, true);
// } else {
// console.error('Not supported file type');
// }
// });
}
});
}
return true;
};
/**
* Keydown event on document
*
* @param {KeyboardEvent} e
*/
this.keydown = (e) => {
const { keyEvent, editable } = this.handler;
if (!Object.keys(keyEvent).length) {
return;
}
const { clipboard, grab } = keyEvent;
if (this.handler.interactionHandler.isDrawingMode()) {
if (this.handler.shortcutHandler.isEscape(e)) {
if (this.handler.interactionMode === 'polygon') {
this.handler.drawingHandler.polygon.finish();
}
else if (this.handler.interactionMode === 'line') {
this.handler.drawingHandler.line.finish();
}
else if (this.handler.interactionMode === 'arrow') {
this.handler.drawingHandler.arrow.finish();
}
else if (this.handler.interactionMode === 'link') {
this.handler.linkHandler.finish();
}
}
return;
}
if (this.handler.shortcutHandler.isW(e) && grab) {
this.code = e.code;
this.handler.interactionHandler.grab();
return;
}
if (e.altKey && editable && grab) {
this.handler.interactionHandler.grab();
return;
}
if (this.handler.shortcutHandler.isEscape(e)) {
if (this.handler.interactionMode === 'selection') {
this.handler.canvas.discardActiveObject();
this.handler.canvas.renderAll();
}
this.handler.tooltipHandler.hide();
}
if (this.handler.canvas.wrapperEl !== document.activeElement) {
return;
}
if (editable) {
if (this.handler.shortcutHandler.isQ(e)) {
this.code = e.code;
}
else if (this.handler.shortcutHandler.isDelete(e)) {
this.handler.remove();
}
else if (this.handler.shortcutHandler.isArrow(e)) {
this.arrowmoving(e);
}
else if (this.handler.shortcutHandler.isCtrlA(e)) {
e.preventDefault();
this.handler.selectAll();
}
else if (this.handler.shortcutHandler.isCtrlC(e)) {
e.preventDefault();
this.handler.copy();
}
else if (this.handler.shortcutHandler.isCtrlV(e) && !clipboard) {
e.preventDefault();
this.handler.paste();
}
else if (this.handler.shortcutHandler.isCtrlX(e)) {
e.preventDefault();
this.handler.cut();
}
else if (this.handler.shortcutHandler.isCtrlZ(e)) {
e.preventDefault();
this.handler.transactionHandler.undo();
}
else if (this.handler.shortcutHandler.isCtrlY(e)) {
e.preventDefault();
this.handler.transactionHandler.redo();
}
else if (this.handler.shortcutHandler.isPlus(e)) {
e.preventDefault();
this.handler.zoomHandler.zoomIn();
}
else if (this.handler.shortcutHandler.isMinus(e)) {
e.preventDefault();
this.handler.zoomHandler.zoomOut();
}
else if (this.handler.shortcutHandler.isO(e)) {
e.preventDefault();
this.handler.zoomHandler.zoomOneToOne();
}
else if (this.handler.shortcutHandler.isP(e)) {
e.preventDefault();
this.handler.zoomHandler.zoomToFit();
}
return;
}
return;
};
/**
* Key up event on canvas
*
* @param {KeyboardEvent} _e
*/
this.keyup = (e) => {
if (this.handler.interactionHandler.isDrawingMode()) {
return;
}
if (!this.handler.shortcutHandler.isW(e)) {
this.handler.interactionHandler.selection();
}
};
/**
* Context menu event on canvas
*
* @param {MouseEvent} e
*/
this.contextmenu = (e) => {
e.preventDefault();
const { editable, onContext } = this.handler;
if (editable && onContext) {
const target = this.handler.canvas.findTarget(e, false);
if (target && target.type !== 'activeSelection') {
this.handler.select(target);
}
this.handler.contextmenuHandler.show(e, target);
}
};
/**
* Mouse down event on canvas
*
* @param {MouseEvent} _e
*/
this.onmousedown = (_e) => {
this.handler.contextmenuHandler.hide();
};
this.handler = handler;
this.initialize();
}
/**
* Attch event on document
*
*/
initialize() {
if (this.handler.editable) {
// @ts-ignore
this.handler.canvas.on({
'object:modified': this.modified,
'object:scaling': this.scaling,
'object:scaled': this.scaled,
'object:moving': this.moving,
'object:moved': this.moved,
'object:rotating': this.rotating,
'object:rotated': this.rotated,
'mouse:wheel': this.mousewheel,
'mouse:down': this.mousedown,
'mouse:move': this.mousemove,
'mouse:up': this.mouseup,
'selection:cleared': this.selection,
'selection:created': this.selection,
'selection:updated': this.selection,
});
}
else {
// @ts-ignore
this.handler.canvas.on({
'mouse:down': this.mousedown,
'mouse:move': this.mousemove,
'mouse:out': this.mouseout,
'mouse:up': this.mouseup,
'mouse:wheel': this.mousewheel,
});
}
this.handler.canvas.wrapperEl.tabIndex = 1000;
this.handler.canvas.wrapperEl.addEventListener('keydown', this.keydown, false);
this.handler.canvas.wrapperEl.addEventListener('keyup', this.keyup, false);
this.handler.canvas.wrapperEl.addEventListener('mousedown', this.onmousedown, false);
this.handler.canvas.wrapperEl.addEventListener('contextmenu', this.contextmenu, false);
if (this.handler.keyEvent.clipboard) {
document.addEventListener('paste', this.paste, false);
}
}
}
exports.default = EventHandler;