UNPKG

react-design-editor

Version:

Design Editor Tools with React.js + ant.design + fabric.js

1,232 lines 67.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const fabric_1 = require("fabric"); const lodash_1 = require("lodash"); const uuidv4_1 = require("uuidv4"); const warning_1 = __importDefault(require("warning")); const _1 = require("."); const CanvasObject_1 = __importDefault(require("../CanvasObject")); const constants_1 = require("../constants"); /** * Main handler for Canvas * @class Handler * @implements {HandlerOptions} */ class Handler { constructor(options) { this.zoomStep = 0.05; this.propertiesToInclude = constants_1.defaults.propertiesToInclude; this.workareaOption = constants_1.defaults.workareaOption; this.canvasOption = constants_1.defaults.canvasOption; this.gridOption = constants_1.defaults.gridOption; this.objectOption = constants_1.defaults.objectOption; this.guidelineOption = constants_1.defaults.guidelineOption; this.keyEvent = constants_1.defaults.keyEvent; this.activeSelectionOption = constants_1.defaults.activeSelectionOption; this.fabricObjects = CanvasObject_1.default; this.handlers = {}; this.objectMap = {}; this.zoom = 1; this.isCut = false; this.isRequsetAnimFrame = false; /** * Init class fields * @param {HandlerOptions} options */ this.initOption = (options) => { this.id = options.id; this.canvas = options.canvas; this.container = options.container; this.editable = options.editable; this.interactionMode = options.interactionMode; this.minZoom = options.minZoom; this.maxZoom = options.maxZoom; this.zoomStep = options.zoomStep || 0.05; this.zoomEnabled = options.zoomEnabled; this.width = options.width; this.height = options.height; this.objects = []; this.setPropertiesToInclude(options.propertiesToInclude); this.setWorkareaOption(options.workareaOption); this.setCanvasOption(options.canvasOption); this.setGridOption(options.gridOption); this.setObjectOption(options.objectOption); this.setFabricObjects(options.fabricObjects); this.setGuidelineOption(options.guidelineOption); this.setActiveSelectionOption(options.activeSelectionOption); this.setKeyEvent(options.keyEvent); }; /** * Initialize callback * @param {HandlerOptions} options */ this.initCallback = (options) => { this.onAdd = options.onAdd; this.onTooltip = options.onTooltip; this.onZoom = options.onZoom; this.onContext = options.onContext; this.onClick = options.onClick; this.onModified = options.onModified; this.onDblClick = options.onDblClick; this.onSelect = options.onSelect; this.onRemove = options.onRemove; this.onTransaction = options.onTransaction; this.onInteraction = options.onInteraction; this.onLoad = options.onLoad; }; /** * Initialize handlers * */ this.initHandler = () => { this.workareaHandler = new _1.WorkareaHandler(this); this.imageHandler = new _1.ImageHandler(this); this.chartHandler = new _1.ChartHandler(this); this.elementHandler = new _1.ElementHandler(this); this.cropHandler = new _1.CropHandler(this); this.animationHandler = new _1.AnimationHandler(this); this.contextmenuHandler = new _1.ContextmenuHandler(this); this.tooltipHandler = new _1.TooltipHandler(this); this.zoomHandler = new _1.ZoomHandler(this, this.zoomStep); this.interactionHandler = new _1.InteractionHandler(this); this.transactionHandler = new _1.TransactionHandler(this); this.gridHandler = new _1.GridHandler(this); this.portHandler = new _1.PortHandler(this); this.linkHandler = new _1.LinkHandler(this); this.nodeHandler = new _1.NodeHandler(this); this.alignmentHandler = new _1.AlignmentHandler(this); this.guidelineHandler = new _1.GuidelineHandler(this); this.eventHandler = new _1.EventHandler(this); this.drawingHandler = new _1.DrawingHandler(this); this.shortcutHandler = new _1.ShortcutHandler(this); }; /** * Get primary object * @returns {FabricObject[]} */ this.getObjects = () => { const objects = this.canvas.getObjects().filter((obj) => { if (obj.id === 'workarea') { return false; } else if (obj.id === 'grid') { return false; } else if (obj.superType === 'port') { return false; } else if (!obj.id) { return false; } return true; }); if (objects.length) { objects.forEach(obj => (this.objectMap[obj.id] = obj)); } else { this.objectMap = {}; } return objects; }; /** * Set key pair * @param {keyof FabricObject} key * @param {*} value * @returns */ this.set = (key, value) => { const activeObject = this.canvas.getActiveObject(); if (!activeObject) { return; } if (activeObject.type === 'svg' && (key === 'fill' || key === 'stroke')) { activeObject._objects.forEach(obj => obj.set(key, value)); } activeObject.set(key, value); activeObject.setCoords(); this.canvas.requestRenderAll(); const { id, superType, type, player, width, height } = activeObject; if (superType === 'element') { if (key === 'visible') { if (value) { activeObject.element.style.display = 'block'; } else { activeObject.element.style.display = 'none'; } } const el = this.elementHandler.findById(id); // update the element this.elementHandler.setScaleOrAngle(el, activeObject); this.elementHandler.setSize(el, activeObject); this.elementHandler.setPosition(el, activeObject); if (type === 'video' && player) { player.setPlayerSize(width, height); } } const { onModified } = this; if (onModified) { onModified(activeObject); } }; /** * Set option * @param {Partial<FabricObject>} option * @returns */ this.setObject = (option) => { const activeObject = this.canvas.getActiveObject(); if (!activeObject) { return; } Object.keys(option).forEach(key => { if (option[key] !== activeObject[key]) { activeObject.set(key, option[key]); activeObject.setCoords(); } }); this.canvas.requestRenderAll(); const { id, superType, type, player, width, height } = activeObject; if (superType === 'element') { if ('visible' in option) { if (option.visible) { activeObject.element.style.display = 'block'; } else { activeObject.element.style.display = 'none'; } } const el = this.elementHandler.findById(id); // update the element this.elementHandler.setScaleOrAngle(el, activeObject); this.elementHandler.setSize(el, activeObject); this.elementHandler.setPosition(el, activeObject); if (type === 'video' && player) { player.setPlayerSize(width, height); } } const { onModified } = this; if (onModified) { onModified(activeObject); } }; /** * Set key pair by object * @param {FabricObject} obj * @param {string} key * @param {*} value * @returns */ this.setByObject = (obj, key, value) => { if (!obj) { return; } if (obj.type === 'svg') { if (key === 'fill') { obj.setFill(value); } else if (key === 'stroke') { obj.setStroke(value); } } obj.set(key, value); obj.setCoords(); this.canvas.renderAll(); const { id, superType, type, player, width, height } = obj; if (superType === 'element') { if (key === 'visible') { if (value) { obj.element.style.display = 'block'; } else { obj.element.style.display = 'none'; } } const el = this.elementHandler.findById(id); // update the element this.elementHandler.setScaleOrAngle(el, obj); this.elementHandler.setSize(el, obj); this.elementHandler.setPosition(el, obj); if (type === 'video' && player) { player.setPlayerSize(width, height); } } const { onModified } = this; if (onModified) { onModified(obj); } }; /** * Set key pair by id * @param {string} id * @param {string} key * @param {*} value */ this.setById = (id, key, value) => { const findObject = this.findById(id); this.setByObject(findObject, key, value); }; /** * Set partial by object * @param {FabricObject} obj * @param {FabricObjectOption} option * @returns */ this.setByPartial = (obj, option) => { if (!obj) { return; } if (obj.type === 'svg') { if (option.fill) { obj.setFill(option.fill); } else if (option.stroke) { obj.setStroke(option.stroke); } } obj.set(option); obj.setCoords(); this.canvas.renderAll(); const { id, superType, type, player, width, height } = obj; if (superType === 'element') { if ('visible' in option) { if (option.visible) { obj.element.style.display = 'block'; } else { obj.element.style.display = 'none'; } } const el = this.elementHandler.findById(id); // update the element this.elementHandler.setScaleOrAngle(el, obj); this.elementHandler.setSize(el, obj); this.elementHandler.setPosition(el, obj); if (type === 'video' && player) { player.setPlayerSize(width, height); } } }; /** * Set shadow * @param {fabric.Shadow} option * @returns */ this.setShadow = (option) => { const activeObject = this.canvas.getActiveObject(); if (!activeObject) { return; } activeObject.set('shadow', new fabric_1.fabric.Shadow(option)); this.canvas.requestRenderAll(); const { onModified } = this; if (onModified) { onModified(activeObject); } }; /** * Set the image * @param {FabricImage} obj * @param {(File | string)} [source] * @param {boolean} [keepSize] Keep size of previous Image * @param {fabric.IImageOptions} [options] * @returns */ this.setImage = (obj, source, keepSize, options) => { const { height, scaleY } = obj; const renderCallbaack = (imgObj, src) => { if (keepSize) { const scale = (height * scaleY) / imgObj.height; imgObj.set({ scaleY: scale, scaleX: scale, src }); } this.canvas.requestRenderAll(); }; return new Promise(resolve => { if (!source) { obj.set('file', null); obj.set('src', null); resolve(obj.setSrc('./images/sample/transparentBg.png', (imgObj) => renderCallbaack(imgObj, null), { dirty: true, ...options, })); } if (source instanceof File) { const reader = new FileReader(); reader.onload = () => { obj.set('file', source); obj.set('src', null); resolve(obj.setSrc(reader.result, (imgObj) => renderCallbaack(imgObj, reader.result), { dirty: true, ...options, })); }; reader.readAsDataURL(source); } else { obj.set('file', null); obj.set('src', source); resolve(obj.setSrc(source, (imgObj) => renderCallbaack(imgObj, source), { dirty: true, crossOrigin: 'anonymous', ...options, })); } }); }; /** * Set image by id * @param {string} id * @param {*} source * @param {boolean} [keepSize] Keep size of previous Image * @returns */ this.setImageById = (id, source, keepSize) => { const findObject = this.findById(id); return Promise.resolve(this.setImage(findObject, source, keepSize)); }; /** * Set Svg * * @param {SvgObject} obj * @param {(File | string)} [source] * @param {boolean} [keepSize] Keep size of previous SVG * @param {boolean} [xmlString] XML string */ this.setSvg = (obj, source, keepSize, xmlString) => { return new Promise(resolve => { if (!source) { resolve(obj.loadSvg({ src: './images/sample/chiller.svg', loadType: 'file', keepSize })); } if (source instanceof File) { const reader = new FileReader(); reader.readAsDataURL(source); reader.onload = () => resolve(obj.loadSvg({ src: reader.result, loadType: 'file', keepSize })); } else { resolve(obj.loadSvg({ src: source, loadType: xmlString ? 'svg' : 'file', keepSize })); } }); }; /** * Set visible * @param {boolean} [visible] * @returns */ this.setVisible = (visible) => { const activeObject = this.canvas.getActiveObject(); if (!activeObject) { return; } if (activeObject.superType === 'element') { if (visible) { activeObject.element.style.display = 'block'; } else { activeObject.element.style.display = 'none'; } } activeObject.set({ visible, }); this.canvas.renderAll(); }; /** * Set the position on Object * * @param {FabricObject} obj * @param {boolean} [centered] */ this.centerObject = (obj, centered) => { if (centered) { this.canvas.centerObject(obj); obj.setCoords(); } else { this.setByPartial(obj, { left: obj.left / this.canvas.getZoom() - obj.width / 2 - this.canvas.viewportTransform[4] / this.canvas.getZoom(), top: obj.top / this.canvas.getZoom() - obj.height / 2 - this.canvas.viewportTransform[5] / this.canvas.getZoom(), }); } }; /** * Add object * @param {FabricObjectOption} obj * @param {boolean} [centered=true] * @param {boolean} [loaded=false] * @param {boolean} [group=false] * @returns */ this.add = (obj, centered = true, loaded = false, group = false) => { const { editable, onAdd, gridOption, objectOption } = this; const option = { hasControls: editable, hasBorders: editable, selectable: editable, lockMovementX: !editable, lockMovementY: !editable, hoverCursor: !editable ? 'pointer' : 'move', }; if (obj.type === 'i-text') { option.editable = false; } else { option.editable = editable; } if (editable && this.workarea.layout === 'fullscreen') { option.scaleX = this.workarea.scaleX; option.scaleY = this.workarea.scaleY; } const newOption = Object.assign({}, objectOption, obj, { container: this.container.id, editable, }, option); // Individually create canvas object if (obj.superType === 'link') { return this.linkHandler.create(newOption, loaded); } let createdObj; // Create canvas object if (obj.type === 'image') { createdObj = this.addImage(newOption); } else if (obj.type === 'group') { createdObj = this.addGroup(newOption); } else { createdObj = this.fabricObjects[obj.type].create(newOption); } if (group) { return createdObj; } this.canvas.add(createdObj); this.objects = this.getObjects(); if (!editable && !(obj.superType === 'element')) { createdObj.on('mousedown', this.eventHandler.object.mousedown); } if (createdObj.dblclick) { createdObj.on('mousedblclick', this.eventHandler.object.mousedblclick); } if (this.objects.some(object => object.type === 'gif')) { this.startRequestAnimFrame(); } else { this.stopRequestAnimFrame(); } if (obj.superType !== 'drawing' && obj.superType !== 'link' && editable && !loaded) { this.centerObject(createdObj, centered); } if (createdObj.superType === 'node') { this.portHandler.create(createdObj); if (createdObj.iconButton) { this.canvas.add(createdObj.iconButton); } } if (!editable && createdObj.animation && createdObj.animation.autoplay) { this.animationHandler.play(createdObj.id); } if (createdObj.superType === 'node') { createdObj.set('shadow', { color: createdObj.stroke, }); } if (gridOption.enabled) { this.gridHandler.setCoords(createdObj); } if (!this.transactionHandler.active && !loaded) { this.transactionHandler.save('add'); } if (onAdd && editable && !loaded) { onAdd(createdObj); } return createdObj; }; /** * Add group object * * @param {FabricGroup} obj * @param {boolean} [centered=true] * @param {boolean} [loaded=false] * @returns */ this.addGroup = (obj) => { const { objects = [], ...other } = obj; const _objects = objects.map(child => this.add(child, false, true, true)); return new fabric_1.fabric.Group(_objects, other); }; /** * Add iamge object * @param {FabricImage} obj * @returns */ this.addImage = (obj) => { const { objectOption } = this; const { filters = [], src, file, ...otherOption } = obj; const image = new Image(); // if (typeof src === 'string') { // image.src = src; // } const createdObj = new fabric_1.fabric.Image(image, { ...objectOption, ...otherOption, }); createdObj.set({ filters: this.imageHandler.createFilters(filters), }); this.setImage(createdObj, src || file); return createdObj; }; /** * Remove object * @param {FabricObject} target * @returns {any} */ this.remove = (target) => { const activeObject = target || this.canvas.getActiveObject(); if (this.prevTarget && this.prevTarget.superType === 'link') { this.linkHandler.remove(this.prevTarget); if (!this.transactionHandler.active) { this.transactionHandler.save('remove'); } return; } if (!activeObject) { return; } if (typeof activeObject.deletable !== 'undefined' && !activeObject.deletable) { return; } if (activeObject.type !== 'activeSelection') { this.canvas.discardActiveObject(); if (activeObject.superType === 'element') { this.elementHandler.removeById(activeObject.id); } if (activeObject.superType === 'link') { this.linkHandler.remove(activeObject); } else if (activeObject.superType === 'node') { if (activeObject.toPort) { if (activeObject.toPort.links.length) { activeObject.toPort.links.forEach((link) => { this.linkHandler.remove(link, 'from'); }); } this.canvas.remove(activeObject.toPort); } if (activeObject.fromPort && activeObject.fromPort.length) { activeObject.fromPort.forEach((port) => { if (port.links.length) { port.links.forEach((link) => { this.linkHandler.remove(link, 'to'); }); } this.canvas.remove(port); }); } } this.canvas.remove(activeObject); } else { const { _objects: activeObjects } = activeObject; const existDeleted = activeObjects.some((obj) => typeof obj.deletable !== 'undefined' && !obj.deletable); if (existDeleted) { return; } this.canvas.discardActiveObject(); activeObjects.forEach((obj) => { if (obj.superType === 'element') { this.elementHandler.removeById(obj.id); } else if (obj.superType === 'node') { if (obj.toPort) { if (obj.toPort.links.length) { obj.toPort.links.forEach((link) => { this.linkHandler.remove(link, 'from'); }); } this.canvas.remove(obj.toPort); } if (obj.fromPort && obj.fromPort.length) { obj.fromPort.forEach((port) => { if (port.links.length) { port.links.forEach((link) => { this.linkHandler.remove(link, 'to'); }); } this.canvas.remove(port); }); } } this.canvas.remove(obj); }); } if (!this.transactionHandler.active) { this.transactionHandler.save('remove'); } this.objects = this.getObjects(); const { onRemove } = this; if (onRemove) { onRemove(activeObject); } }; /** * Remove object by id * @param {string} id */ this.removeById = (id) => { const findObject = this.findById(id); if (findObject) { this.remove(findObject); } }; /** * Delete at origin object list * @param {string} id */ this.removeOriginById = (id) => { const object = this.findOriginByIdWithIndex(id); if (object.index > 0) { this.objects.splice(object.index, 1); } }; /** * Duplicate object * @returns */ this.duplicate = () => { const { onAdd, propertiesToInclude, gridOption: { grid = 10 }, } = this; const activeObject = this.canvas.getActiveObject(); if (!activeObject) { return; } if (typeof activeObject.cloneable !== 'undefined' && !activeObject.cloneable) { return; } activeObject.clone((clonedObj) => { this.canvas.discardActiveObject(); clonedObj.set({ left: clonedObj.left + grid, top: clonedObj.top + grid, evented: true, }); if (clonedObj.type === 'activeSelection') { const activeSelection = clonedObj; activeSelection.canvas = this.canvas; activeSelection.forEachObject((obj) => { obj.set('id', (0, uuidv4_1.uuid)()); if (obj.superType === 'node') { obj.set('shadow', { color: obj.stroke, }); } this.canvas.add(obj); this.objects = this.getObjects(); if (obj.dblclick) { obj.on('mousedblclick', this.eventHandler.object.mousedblclick); } }); if (onAdd) { onAdd(activeSelection); } activeSelection.setCoords(); } else { if (activeObject.id === clonedObj.id) { clonedObj.set('id', (0, uuidv4_1.uuid)()); } if (clonedObj.superType === 'node') { clonedObj.set('shadow', { color: clonedObj.stroke, }); } this.canvas.add(clonedObj); this.objects = this.getObjects(); if (clonedObj.dblclick) { clonedObj.on('mousedblclick', this.eventHandler.object.mousedblclick); } if (onAdd) { onAdd(clonedObj); } } this.canvas.setActiveObject(clonedObj); this.portHandler.create(clonedObj); this.canvas.requestRenderAll(); }, propertiesToInclude); }; /** * Duplicate object by id * @param {string} id * @returns */ this.duplicateById = (id) => { const { onAdd, propertiesToInclude, gridOption: { grid = 10 }, } = this; const findObject = this.findById(id); if (findObject) { if (typeof findObject.cloneable !== 'undefined' && !findObject.cloneable) { return false; } findObject.clone((cloned) => { cloned.set({ left: cloned.left + grid, top: cloned.top + grid, id: (0, uuidv4_1.uuid)(), evented: true, }); this.canvas.add(cloned); this.objects = this.getObjects(); if (onAdd) { onAdd(cloned); } if (cloned.dblclick) { cloned.on('mousedblclick', this.eventHandler.object.mousedblclick); } this.canvas.setActiveObject(cloned); this.portHandler.create(cloned); this.canvas.requestRenderAll(); }, propertiesToInclude); } return true; }; /** * Cut object * */ this.cut = () => { this.copy(); this.remove(); this.isCut = true; }; /** * Copy to clipboard * * @param {*} value */ this.copyToClipboard = (value) => { const textarea = document.createElement('textarea'); document.body.appendChild(textarea); textarea.value = value; textarea.select(); document.execCommand('copy'); document.body.removeChild(textarea); this.canvas.wrapperEl.focus(); }; /** * Copy object * * @returns */ this.copy = () => { const { propertiesToInclude } = this; const activeObject = this.canvas.getActiveObject(); if (activeObject && activeObject.superType === 'link') { return false; } if (activeObject) { if (typeof activeObject.cloneable !== 'undefined' && !activeObject.cloneable) { return false; } if (activeObject.type === 'activeSelection') { const activeSelection = activeObject; if (activeSelection.getObjects().some((obj) => obj.superType === 'node')) { if (this.keyEvent.clipboard) { const links = []; const objects = activeSelection.getObjects().map((obj, index) => { if (typeof obj.cloneable !== 'undefined' && !obj.cloneable) { return null; } if (obj.fromPort.length) { obj.fromPort.forEach((port) => { if (port.links.length) { port.links.forEach((link) => { const linkTarget = { fromNodeIndex: index, fromPortId: port.id, type: 'curvedLink', superType: 'link', }; const findIndex = activeSelection .getObjects() .findIndex((compObj) => compObj.id === link.toNode.id); if (findIndex >= 0) { linkTarget.toNodeIndex = findIndex; links.push(linkTarget); } }); } }); } return { name: obj.name, description: obj.description, superType: obj.superType, type: obj.type, nodeClazz: obj.nodeClazz, configuration: obj.configuration, descriptor: obj.descriptor, properties: { left: activeObject.left + activeObject.width / 2 + obj.left || 0, top: activeObject.top + activeObject.height / 2 + obj.top || 0, iconName: obj.descriptor.icon, }, }; }); this.copyToClipboard(JSON.stringify(objects.concat(links), null, '\t')); return true; } this.clipboard = activeObject; return true; } } activeObject.clone((cloned) => { if (this.keyEvent.clipboard) { if (cloned.superType === 'node') { const node = { name: cloned.name, description: cloned.description, superType: cloned.superType, type: cloned.type, nodeClazz: cloned.nodeClazz, configuration: cloned.configuration, descriptor: cloned.descriptor, properties: { left: cloned.left || 0, top: cloned.top || 0, iconName: cloned.descriptor.icon, }, }; const objects = [node]; this.copyToClipboard(JSON.stringify(objects, null, '\t')); } else { this.copyToClipboard(JSON.stringify(cloned.toObject(propertiesToInclude), null, '\t')); } } else { this.clipboard = cloned; } }, propertiesToInclude); } return true; }; /** * Paste object * * @returns */ this.paste = () => { const { onAdd, propertiesToInclude, gridOption: { grid = 10 }, clipboard, isCut, } = this; const padding = isCut ? 0 : grid; if (!clipboard) { return false; } if (typeof clipboard.cloneable !== 'undefined' && !clipboard.cloneable) { return false; } this.isCut = false; if (clipboard.type === 'activeSelection') { if (clipboard.getObjects().some((obj) => obj.superType === 'node')) { this.canvas.discardActiveObject(); const objects = []; const linkObjects = []; clipboard.getObjects().forEach((obj) => { if (typeof obj.cloneable !== 'undefined' && !obj.cloneable) { return; } const clonedObj = obj.duplicate(); if (clonedObj.type === 'SwitchNode') { clonedObj.set({ left: obj.left + padding + padding, top: obj.top + padding, }); } else { clonedObj.set({ left: obj.left + padding, top: obj.top + padding, }); } if (obj.fromPort.length) { obj.fromPort.forEach((port) => { if (port.links.length) { port.links.forEach((link) => { const linkTarget = { fromNode: clonedObj.id, fromPort: port.id, }; const findIndex = clipboard .getObjects() .findIndex((compObj) => compObj.id === link.toNode.id); if (findIndex >= 0) { linkTarget.toNodeIndex = findIndex; linkObjects.push(linkTarget); } }); } }); } if (clonedObj.dblclick) { clonedObj.on('mousedblclick', this.eventHandler.object.mousedblclick); } this.canvas.add(clonedObj); this.objects = this.getObjects(); this.portHandler.create(clonedObj); objects.push(clonedObj); }); if (linkObjects.length) { linkObjects.forEach((linkObject) => { const { fromNode, fromPort, toNodeIndex } = linkObject; const toNode = objects[toNodeIndex]; const link = { type: 'curvedLink', fromNodeId: fromNode, fromPortId: fromPort, toNodeId: toNode.id, toPortId: toNode.toPort.id, }; this.linkHandler.create(link); }); } const activeSelection = new fabric_1.fabric.ActiveSelection(objects, { canvas: this.canvas, ...this.activeSelectionOption, }); if (isCut) { this.clipboard = null; } else { this.clipboard = activeSelection; } if (!this.transactionHandler.active) { this.transactionHandler.save('paste'); } this.canvas.setActiveObject(activeSelection); this.canvas.renderAll(); return true; } } clipboard.clone((clonedObj) => { this.canvas.discardActiveObject(); clonedObj.set({ left: clonedObj.left + padding, top: clonedObj.top + padding, id: isCut ? clipboard.id : (0, uuidv4_1.uuid)(), evented: true, }); if (clonedObj.type === 'activeSelection') { clonedObj.canvas = this.canvas; clonedObj.forEachObject((obj) => { obj.set('id', isCut ? obj.id : (0, uuidv4_1.uuid)()); this.canvas.add(obj); if (obj.dblclick) { obj.on('mousedblclick', this.eventHandler.object.mousedblclick); } }); } else { if (clonedObj.superType === 'node') { clonedObj = clonedObj.duplicate(); } this.canvas.add(clonedObj); if (clonedObj.dblclick) { clonedObj.on('mousedblclick', this.eventHandler.object.mousedblclick); } } const newClipboard = clipboard.set({ top: clonedObj.top, left: clonedObj.left, }); if (isCut) { this.clipboard = null; } else { this.clipboard = newClipboard; } if (clonedObj.superType === 'node') { this.portHandler.create(clonedObj); } if (!this.transactionHandler.active) { this.transactionHandler.save('paste'); } // TODO... // After toGroup svg elements not rendered. this.objects = this.getObjects(); if (onAdd) { onAdd(clonedObj); } clonedObj.setCoords(); this.canvas.setActiveObject(clonedObj); this.canvas.requestRenderAll(); }, propertiesToInclude); return true; }; /** * Find object by object * @param {FabricObject} obj */ this.find = (obj) => this.findById(obj.id); /** * Find object by id * @param {string} id * @returns {(FabricObject | null)} */ this.findById = (id) => { let findObject; const exist = this.objects.some(obj => { if (obj.id === id) { findObject = obj; return true; } return false; }); if (!exist) { (0, warning_1.default)(true, 'Not found object by id.'); return null; } return findObject; }; /** * Find object in origin list * @param {string} id * @returns */ this.findOriginById = (id) => { let findObject; const exist = this.objects.some(obj => { if (obj.id === id) { findObject = obj; return true; } return false; }); if (!exist) { console.warn('Not found object by id.'); return null; } return findObject; }; /** * Return origin object list * @param {string} id * @returns */ this.findOriginByIdWithIndex = (id) => { let findObject; let index = -1; const exist = this.objects.some((obj, i) => { if (obj.id === id) { findObject = obj; index = i; return true; } return false; }); if (!exist) { console.warn('Not found object by id.'); return {}; } return { object: findObject, index, }; }; /** * Select object * @param {FabricObject} obj * @param {boolean} [find] */ this.select = (obj, find) => { let findObject = obj; if (find) { findObject = this.find(obj); } if (findObject) { this.canvas.discardActiveObject(); this.canvas.setActiveObject(findObject); this.canvas.requestRenderAll(); } }; /** * Select by id * @param {string} id */ this.selectById = (id) => { const findObject = this.findById(id); if (findObject) { this.canvas.discardActiveObject(); this.canvas.setActiveObject(findObject); this.canvas.requestRenderAll(); } }; /** * Select all * @returns */ this.selectAll = () => { this.canvas.discardActiveObject(); const filteredObjects = this.canvas.getObjects().filter((obj) => { if (obj.id === 'workarea') { return false; } else if (!obj.evented) { return false; } else if (obj.superType === 'link') { return false; } else if (obj.superType === 'port') { return false; } else if (obj.superType === 'element') { return false; } else if (obj.locked) { return false; } return true; }); if (!filteredObjects.length) { return; } if (filteredObjects.length === 1) { this.canvas.setActiveObject(filteredObjects[0]); this.canvas.renderAll(); return; } const activeSelection = new fabric_1.fabric.ActiveSelection(filteredObjects, { canvas: this.canvas, ...this.activeSelectionOption, }); this.canvas.setActiveObject(activeSelection); this.canvas.renderAll(); }; /** * Save origin width, height * @param {FabricObject} obj * @param {number} width * @param {number} height */ this.originScaleToResize = (obj, width, height) => { if (obj.id === 'workarea') { this.setByPartial(obj, { workareaWidth: obj.width, workareaHeight: obj.height, }); } this.setByPartial(obj, { scaleX: width / obj.width, scaleY: height / obj.height, }); }; /** * When set the width, height, Adjust the size * @param {number} width * @param {number} height */ this.scaleToResize = (width, height) => { const activeObject = this.can