UNPKG

flowappplatform-appbuilder-graphs

Version:
1,224 lines (1,211 loc) 58.4 kB
import { createElement, Component, Fragment } from 'react'; import { Wedge, Group, Circle, Image as Image$1, Rect, Text, Arrow, Stage, Layer, FastLayer } from 'react-konva'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var start = require('flow-start-component/components.json'); var api = require('flow-api-component/components'); var cloudboost = require('flow-cloudboost-component/components'); var if_else = require('flow-if-else-component/components'); var json = require('flow-json-component/components'); var stripe = require('flow-stripe-component/components'); var text = require('flow-text-component/components'); var twilio = require('flow-twilio-component/components'); var mailjet = require('flow-mailjet-component/components'); var insert_object = require('flow-insert-object-component/components'); var keyBy = require("lodash").keyBy; var componentDefinition = [ { graphComponentId: "smart-segment", title: "Smart Segment Trigger", category: "Trigger", imgSrc: "/assets/images/icons/smart-segment.svg", type: "graphComponent", metadata: { "icon-color": "#7ab260" }, ports: [ { id: "smart-segment-a", title: "Smart Segment Trigger A", description: "Simple description", color: "blue", arcColor: "blue" }, { id: "smart-segment-b", title: "Smart Segment Trigger B", description: "Simple description", color: "yellow" }, { id: "smart-segment-c", title: "Smart Segment Trigger C", description: "Simple description", color: "green" }, { id: "smart-segment-d", title: "Smart Segment Trigger D", description: "Simple description", color: "lightblue" }, { id: "smart-segment-e", title: "Smart Segment Trigger E", description: "Simple description", color: "#7ab260" } ], properties: [ { categoryName: "General", categoryDescription: "Basic settings about this app.", properties: [ { id: "title", name: "App Name", type: "text", options: {}, data: null }, { id: "description", name: "App Description", type: "description", options: {}, data: null } ] }, { categoryName: "Style and Branding", categoryDescription: "Change style and branding of your app", properties: [ { id: "square-logo", name: "Square Logo", type: "image", options: { sizeRequirement: "square" }, data: null } ] } ], showOnComponentsPanel: false, isDeletable: false }, { graphComponentId: "livechat-tag", title: "LiveChat Tag", category: "Trigger", imgSrc: "/assets/images/icons/liveChat.svg", type: "graphComponent", metadata: { "icon-color": "#d1683a" }, ports: [ { id: "livechat-tag-a", title: "Live Chat A", description: "Simple description", color: "rgba(0, 0, 255, 0.5)", arcColor: "rgba(0, 0, 255)" }, { id: "livechat-tag-b", title: "Live Chat B", description: "Simple description", color: "yellow" }, { id: "livechat-tag-c", title: "Live Chat C", description: "Simple description", color: "#279B04", arcColor: "#145104" }, { id: "livechat-tag-d", title: "Live Chat D", description: "Simple description", color: "#7ab260" }, { id: "livechat-tag-e", title: "Live Chat E", description: "Simple description", color: "#d1683a" } ], properties: [ { categoryName: "General", categoryDescription: "Basic settings about this app.", properties: [ { id: "title", name: "App Name", type: "text", options: {}, data: null }, { id: "description", name: "App Description", type: "description", options: {}, data: null } ] }, { categoryName: "Style and Branding", categoryDescription: "Change style and branding of your app", properties: [ { id: "square-logo", name: "Square Logo", type: "image", options: { sizeRequirement: "square" }, data: null } ] } ], showOnComponentsPanel: true, isDeletable: true }, { graphComponentId: "lead-ad-submitted", title: "Lead Ad Submitted", category: "Trigger", imgSrc: "/assets/images/icons/fb_lead_ads.svg", type: "graphComponent", metadata: { "icon-color": "#4460a0" }, ports: [ { id: "lead-ad-submitted-a", title: "Lead Ad Submitted Trigger A", description: "Simple description", color: "blue" }, { id: "lead-ad-submitted-b", title: "Lead Ad Submitted Trigger B", description: "Simple description", color: "yellow" }, { id: "lead-ad-submitted-c", title: "Lead Ad Submitted Trigger C", description: "Simple description", color: "green" } ], properties: [ { categoryName: "General", categoryDescription: "Basic settings about this app.", properties: [ { id: "title", name: "App Name", type: "text", options: {}, data: null }, { id: "description", name: "App Description", type: "description", options: {}, data: null } ] }, { categoryName: "Style and Branding", categoryDescription: "Change style and branding of your app", properties: [ { id: "square-logo", name: "Square Logo", type: "image", options: { sizeRequirement: "square" }, data: null } ] } ], showOnComponentsPanel: true, isDeletable: true }, { graphComponentId: "delay", title: "Add Delay", category: "Actions", imgSrc: "/assets/images/icons/trigger-timer.svg", type: "graphComponent", metadata: { "icon-color": "#fb7138" }, ports: [ { id: "delay-a", title: "Add Delay A", description: "Simple description", color: "#00D4AB" } ], properties: [ { categoryName: "General", categoryDescription: "Basic settings about this app.", properties: [ { id: "title", name: "App Name", type: "text", options: {}, data: null }, { id: "description", name: "App Description", type: "description", options: {}, data: null } ] }, { categoryName: "Style and Branding", categoryDescription: "Change style and branding of your app", properties: [ { id: "square-logo", name: "Square Logo", type: "image", options: { sizeRequirement: "square" }, data: null } ] } ], showOnComponentsPanel: true, isDeletable: true }, { graphComponentId: "send-mail", title: "Send Email", category: "Actions", imgSrc: "/assets/images/icons/email.svg", type: "graphComponent", metadata: { "icon-color": "#f0b029" }, ports: [ { id: "send-mail-a", title: "Send Email A", description: "Simple description", color: "blue" }, { id: "send-mail-b", title: "Send Email B", description: "Simple description", color: "#f0b029" }, { id: "send-mail-c", title: "Send Email C", description: "Simple description", color: "green" }, { id: "send-mail-d", title: "Send Email D", description: "Simple description", color: "lightblue" } ], properties: [ { categoryName: "General", categoryDescription: "Basic settings about this app.", properties: [ { id: "title", name: "App Name", type: "text", options: {}, data: null }, { id: "description", name: "App Description", type: "description", options: {}, data: null } ] }, { categoryName: "Style and Branding for Email Component", categoryDescription: "Change style and branding of your app", properties: [ { id: "square-logo", name: "Square Logo", type: "image", options: { sizeRequirement: "square" }, data: null } ] } ], showOnComponentsPanel: true, isDeletable: true }, { graphComponentId: "send-sms", title: "Send SMS", category: "Actions", imgSrc: "/assets/images/icons/twilio.svg", type: "graphComponent", metadata: { "icon-color": "#f22f46" }, ports: [ { id: "send-sms-a", title: "Send Sms A", description: "Simple description", color: "red" }, { id: "send-sms-b", title: "Send Sms B", description: "Simple description", color: "blue" }, { id: "send-sms-c", title: "Send Sms C", description: "Simple description", color: "pink" } ], properties: [ { categoryName: "General", categoryDescription: "Basic settings about this app.", properties: [ { id: "title", name: "App Name", type: "text", options: {}, data: null }, { id: "description", name: "App Description", type: "description", options: {}, data: null } ] }, { categoryName: "Style and Branding", categoryDescription: "Change style and branding of your app", properties: [ { id: "square-logo", name: "Square Logo", type: "image", options: { sizeRequirement: "square" }, data: null } ] } ], showOnComponentsPanel: true, isDeletable: true }, { graphComponentId: "has-submitted-typeform", title: "Has Submitted Typeform", category: "Conditions", imgSrc: "/assets/images/icons/typeform.svg", type: "graphComponent", metadata: { "icon-color": "#262627" }, ports: [ { id: "has-submitted-typeform-a", title: "Has Submitted Typeform A", description: "Simple description", color: "#262627" }, { id: "has-submitted-typeform-b", title: "Has Submitted Typeform B", description: "Simple description", color: "yellow" }, { id: "has-submitted-typeform-c", title: "Has Submitted Typeform C", description: "Simple description", color: "green" } ], properties: [ { categoryName: "General", categoryDescription: "Basic settings about this app.", properties: [ { id: "title", name: "App Name", type: "text", options: {}, data: null }, { id: "description", name: "App Description", type: "description", options: {}, data: null } ] }, { categoryName: "Style and Branding", categoryDescription: "Change style and branding of your app", properties: [ { id: "square-logo", name: "Square Logo", type: "image", options: { sizeRequirement: "square" }, data: null } ] } ], showOnComponentsPanel: true, isDeletable: true }, { graphComponentId: "check-deal-stage", title: "Check Deal Stage", category: "Conditions", imgSrc: "/assets/images/icons/pipedrive.svg", type: "graphComponent", metadata: { "icon-color": "#26292a" }, ports: [ { id: "check-deal-stage-a", title: "Check Deal Stage A", description: "Simple description", color: "#26292a" }, { id: "check-deal-stage-b", title: "Check Deal Stage B", description: "Simple description", color: "yellow" }, { id: "check-deal-stage-c", title: "Check Deal Stage C", description: "Simple description", color: "green" }, { id: "check-deal-stage-d", title: "Check Deal Stage D", description: "Simple description", color: "lightblue" } ], properties: [ { categoryName: "General", categoryDescription: "Basic settings about this app.", properties: [ { id: "title", name: "App Name", type: "text", options: {}, data: null }, { id: "description", name: "App Description", type: "description", options: {}, data: null } ] }, { categoryName: "Style and Branding", categoryDescription: "Change style and branding of your app", properties: [ { id: "square-logo", name: "Square Logo", type: "image", options: { sizeRequirement: "square" }, data: null } ] } ], showOnComponentsPanel: true, isDeletable: true }, { graphComponentId: "check-headsup-status", title: "Check Headsup Status", category: "Conditions", imgSrc: "/assets/images/icons/headsup.svg", type: "graphComponent", metadata: { "icon-color": "#ee336d" }, ports: [ { id: "check-headsup-a", title: "Check Headsup Status A", description: "Simple description", color: "#26292a" }, { id: "check-headsup-b", title: "Check Headsup Status B", description: "Simple description", color: "yellow" }, { id: "check-headsup-c", title: "Check Headsup Status C", description: "Simple description", color: "#ee336d" }, { id: "check-headsup-d", title: "Check Headsup Status D", description: "Simple description", color: "lightblue" } ], properties: [ { categoryName: "General", categoryDescription: "Basic settings about this app.", properties: [ { id: "title", name: "App Name", type: "text", options: {}, data: null }, { id: "description", name: "App Description", type: "description", options: {}, data: null } ] }, { categoryName: "Style and Branding", categoryDescription: "Change style and branding of your app", properties: [ { id: "square-logo", name: "Square Logo", type: "image", options: { sizeRequirement: "square" }, data: null } ] } ], showOnComponentsPanel: true, isDeletable: true } ]; var components = componentDefinition.concat(Object.values(Object.assign({}, start, api, cloudboost, json, if_else, insert_object, mailjet, start, stripe, text, twilio))); var componentsByType = keyBy(components, "graphComponentId"); /** * @class Port */ var Port = /** @class */ (function (_super) { __extends(Port, _super); function Port() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.state = { hovering: false }; _this.onHoverChange = function (hovering) { _this.setState({ hovering: hovering }); }; _this.onText = function (text) { if (!_this.text) { _this.text = text; var height = _this.text.getHeight(); _this.text.offsetY(height / 2); _this.text.moveToTop(); } }; return _this; } Port.prototype.render = function () { var _this = this; var _a = this.props, port = _a.port, angle = _a.angle, idx = _a.idx, onHover = _a.onHover, visible = _a.visible; var rotation = -90 + angle * idx; var radius = 38; return (createElement(Fragment, null, createElement(Wedge, { angle: angle, radius: radius, fill: port.color, rotation: rotation, visible: visible, onMouseOver: function (ev) { _this.onHoverChange(true); ev.cancelBubble = true; onHover(port); }, onMouseOut: function () { return _this.onHoverChange(false); }, onDragMove: function (ev) { ev.cancelBubble = true; }, onDragStart: function (ev) { ev.cancelBubble = true; } }))); }; return Port; }(Component)); /** * @class Connector */ var Options = { iconWidth: 20, iconHeight: 20 }; var Connector = /** @class */ (function (_super) { __extends(Connector, _super); function Connector() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.state = { hovering: false, connectorCentreClicked: false, connectorImageLoaded: false }; _this.onHoverChange = function (hovering) { _this.setState({ hovering: hovering }); }; _this.handleMouseDown = function () { var _a = _this.props, onPortHovered = _a.onPortHovered, component = _a.component; var definition = components.find(function (c) { return component.graphComponentId === c.graphComponentId; }); var length = definition && definition.ports.length; if (length && length <= 1) { if (definition && length === 1) { var portSel_1 = definition.ports[0]; setTimeout(function () { onPortHovered(portSel_1); _this.setState({ connectorCentreClicked: false }); }, 50); } } else if (length && length > 1) { _this.setState({ connectorCentreClicked: true }); } }; return _this; } Connector.prototype.componentDidMount = function () { var _this = this; this.connectorImage = new Image(Options.iconWidth); this.connectorImage.onload = function () { _this.setState({ connectorImageLoaded: true }); }; this.connectorImage.src = "/assets/images/arrow-right-white.svg"; }; Connector.prototype.render = function () { var _this = this; var _a = this.props, component = _a.component, x = _a.x, y = _a.y, onPortHovered = _a.onPortHovered, mouseIsOver = _a.mouseIsOver, newConnectionInProgress = _a.newConnectionInProgress, selectedComponentId = _a.selectedComponentId; var definition = components.find(function (c) { return component.graphComponentId === c.graphComponentId; }); if (!definition) { return null; } var angle = 180 / definition.ports.length; var selected = selectedComponentId === component.id; var showCircle = this.props.mouseIsOver; if (newConnectionInProgress) { showCircle = selected; } return (createElement(Group, { x: x, y: y }, createElement(Group, { visible: mouseIsOver || newConnectionInProgress }, definition.ports.map(function (port, idx) { return (createElement(Port, __assign({ port: port, angle: angle, idx: idx, key: "port-" + component.id + "-" + port.title }, (_this.state.connectorCentreClicked ? { onHover: function (e) { onPortHovered(e); _this.setState({ connectorCentreClicked: false }); } } : { onHover: function () { } }), { visible: _this.state.connectorCentreClicked || (selected && newConnectionInProgress) }))); }), createElement(Group, { visible: showCircle, onMouseDown: this.handleMouseDown }, createElement(Circle, { radius: 18, fill: "skyblue", stroke: "white", strokeWidth: 2 }), this.state.connectorImageLoaded && (createElement(Image$1, { image: this.connectorImage, offsetX: 18, offsetY: 10, width: Options.iconWidth, height: Options.iconHeight })))))); }; return Connector; }(Component)); var startComponentId = 'start'; var componentWidth = 140; var componentHeight = 140; var computeXAngle = function (fromX, fromY, toX, toY) { var height = toY - fromY; var width = toX - fromX; var hypotenuse = Math.sqrt(height * height + width * width); return Math.acos(width / hypotenuse) * 180 / Math.PI; }; /** * @class Component */ var Options$1 = { componentWidth: componentWidth, componentHeight: componentHeight, iconWidth: 35, iconHeight: 25 }; var Component$1 = /** @class */ (function (_super) { __extends(Component$$1, _super); function Component$$1() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.state = { imageLoaded: false, closeImageLoaded: false, infoImageLoaded: false, mouseIsOver: false }; _this.componentMoved = function (ev) { var x = ev.target.getAttr("x"); var y = ev.target.getAttr("y"); _this.props.onComponentMoved(_this.props.component, x, y); }; _this.componentMoveFinished = function (ev) { _this.props.onComponentMoveFinished(_this.props.component); }; _this.onPortHovered = function (port) { _this.props.onConnectionStart(_this.props.component, port); }; _this.onText = function (text) { if (!_this.text) { _this.text = text; } if (text) { var width = _this.text.getWidth(); /* (this.text as KonvaText).offsetX(width / 2); (this.text as KonvaText).moveToTop(); */ var padding = Math.abs((text.height() - (componentHeight / 2)) / 2); text.y(_this.props.component.y + padding); // text.x(this.props.component.x); // console.info('onText', text.text(), text.height(), this.props.component, padding); } }; _this.componentDeleted = function () { _this.props.onComponentDeleted(_this.props.component); }; _this.componentMousedOver = function (event) { event.cancelBubble = true; _this.setState({ mouseIsOver: true }); _this.props.onComponentMousedOver(_this.props.component, 0, 0); }; _this.componentMousedOut = function (event) { event.cancelBubble = true; _this.setState({ mouseIsOver: false }); _this.props.onComponentMousedOut(_this.props.component); }; return _this; } Component$$1.prototype.componentDidMount = function () { var _this = this; // definition var component = this.props.component; this.definition = components.find(function (c) { return component.graphComponentId === c.graphComponentId; }); this.closeImage = new Image(Options$1.iconWidth); this.closeImage.onload = function () { _this.setState({ closeImageLoaded: true }); }; this.closeImage.src = "/assets/images/close-white.svg"; this.infoImage = new Image(Options$1.iconWidth); this.infoImage.onload = function () { _this.setState({ infoImageLoaded: true }); }; this.infoImage.src = "/assets/images/info-white.svg"; // image loading if (this.definition) { this.image = new Image(Options$1.iconWidth); this.image.onload = function () { _this.setState({ imageLoaded: true }); }; this.image.src = this.definition.imgSrc; } }; Component$$1.prototype.render = function () { var _this = this; var _a = this.props, component = _a.component, newConnectionInProgress = _a.newConnectionInProgress, selectedComponentId = _a.selectedComponentId; var definition = this.definition; if (!definition) { return null; } var width = Options$1.componentWidth; var height = Options$1.componentHeight; var connectorX = component.x + width / 2; var connectorY = component.y; var selected = selectedComponentId === this.props.component.id; var showPeripherals = this.state.mouseIsOver; if (newConnectionInProgress) { showPeripherals = false; // selected && this.state.mouseIsOver; } return (createElement(Fragment, null, createElement(Group, { name: component.id, onDragMove: this.componentMoved, onDragEnd: this.componentMoveFinished, onMouseOver: this.componentMousedOver, onMouseOut: this.componentMousedOut, ref: function (node) { // deal with cursors // node.container().style.cursor = ''; } }, createElement(Rect, { draggable: true, cornerRadius: 5, name: component.id, x: component.x, y: component.y, height: height, width: width, fill: definition.metadata["icon-color"], offsetX: width / 2, offsetY: height / 2, shadowBlur: 20, shadowOpacity: 0.4 }), this.state.imageLoaded && (createElement(Image$1, { image: this.image, x: component.x, y: component.y - 45, width: 45, height: 45, offsetX: 23, listening: false, opacity: 1 })), createElement(Text, { width: componentWidth - 14, align: "center", fontSize: 14, lineHeight: 1.3, fill: "white", x: component.x, offsetX: componentWidth / 2 - 7, ref: function (text) { if (text === null) return; var padding = Math.abs((text.height() - (componentHeight / 2)) / 2); text.y(_this.props.component.y + padding); }, text: definition.title, listening: false }), createElement(Group, { onClick: this.componentDeleted, x: component.x - Options$1.componentWidth / 2, y: component.y - Options$1.componentHeight / 2, visible: showPeripherals && component.id !== startComponentId }, createElement(Circle, { fill: "red", radius: 12, shadowBlur: 10, shadowOpacity: 0.5, stroke: "white", strokeWidth: 1 }), this.state.closeImageLoaded && (createElement(Image$1, { image: this.closeImage, offsetX: 5, offsetY: 5, width: 10, height: 10 }))), createElement(Group, { onClick: function () { return _this.props.onComponentSelected(component); }, x: component.x + Options$1.componentWidth / 2, y: component.y - Options$1.componentHeight / 2 }, createElement(Circle, { fill: "orange", radius: 12, shadowBlur: 10, shadowOpacity: 0.5, stroke: "white", strokeWidth: 1 }), this.state.infoImageLoaded && (createElement(Image$1, { image: this.infoImage, offsetX: 6, offsetY: 6, width: 12, height: 12 }))), createElement(Connector, { x: connectorX, y: connectorY, component: component, selectedComponentId: selectedComponentId, mouseIsOver: this.state.mouseIsOver, newConnectionInProgress: newConnectionInProgress, onPortHovered: this.onPortHovered })))); }; return Component$$1; }(Component)); /** * @class Connection */ var Options$2 = { componentWidth: componentWidth, componentHeight: componentHeight, iconWidth: 35, iconHeight: 25 }; var Connection = /** @class */ (function (_super) { __extends(Connection, _super); function Connection() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.state = { hovering: false, mouseX: 0, mouseY: 0, closeImageLoaded: false }; _this.onNode = function (node) { if (!_this.node) { _this.node = node; } if (_this.node) { _this.node.moveToBottom(); } }; _this.onHoverChange = function (hovering) { if (hovering) { try { var _a = _this.getScaledPointerPosition(), mouseX = _a.x, mouseY = _a.y; _this.setState({ hovering: hovering, mouseX: mouseX, mouseY: mouseY }); } catch (e) { /* ignore */ } } else { setTimeout(function () { _this.setState({ hovering: hovering }); }, 200); } }; _this.getScaledPointerPosition = function () { var stage = _this.node.getStage(); var pointerPosition = stage.getPointerPosition(); var stageX = stage.getAttr("x"); var stageY = stage.getAttr("y"); var stageScaleX = stage.getAttr("scaleX"); var stageScaleY = stage.getAttr("scaleY"); var x = (pointerPosition.x - stageX) / stageScaleX; var y = (pointerPosition.y - stageY) / stageScaleY; return { x: x, y: y }; }; _this.onText = function (text, i) { var props = _this.props; if (props.newConnection) return; if (text) { var spacing = 5; // spacing between labels var tCount = props.connectionsSharingPath.length; var tHWidth = text.getWidth() / 2; var offsetX = tHWidth; if (tCount > 2) { var indexEven = (1 + i) % 2 === 0; offsetX = tCount % 2 === 0 ? tHWidth - (i * (spacing + tHWidth)) + ((spacing + tHWidth) * (indexEven ? tCount / 2 : (tCount / 2) - 1)) : tHWidth - (i * (spacing + tHWidth)) + (tHWidth * tCount / 2); } text.offsetX(offsetX); } }; return _this; } Connection.prototype.componentDidMount = function () { var _this = this; try { var linePosition = this.getScaledPointerPosition(); if (linePosition) { this.setState({ mouseX: linePosition.x, mouseY: linePosition.y }); } } catch (e) { /* ignore */ } this.closeImage = new Image(Options$2.iconWidth); this.closeImage.onload = function () { _this.setState({ closeImageLoaded: true }); }; this.closeImage.src = "/assets/images/close-white.svg"; }; Connection.prototype.render = function () { var _this = this; var props = this.props; if (props.fromComponent === undefined) return null; // const definition = componentsByType[props.fromComponent.graphComponentId]; // const { ports } = definition; var fromX = props.fromComponent.x; var fromY = props.fromComponent.y; var toX = fromX; var toY = fromY; if (props.newConnection) { if (props.componentMousedOver === null) { toX = props.mousePosition.x !== -1 ? props.mousePosition.x : props.fromComponent.x; toY = props.mousePosition.y !== -1 ? props.mousePosition.y : props.fromComponent.y; } else { toX = props.componentMousedOver.x; toY = props.componentMousedOver.y; } } else if (props.toComponent) { toX = props.toComponent.x; toY = props.toComponent.y; } else return null; var height = toY - fromY; var width = toX - fromX; var connectionAngle = computeXAngle(fromX, fromY, toX, toY); var connectionAngleX = connectionAngle; var connectionAngleY = connectionAngle; var connectionAngleForText = connectionAngle; if (toX < fromX) { connectionAngleForText = connectionAngleForText - 180; connectionAngleY = Math.abs(connectionAngleY - 180); } if (toY < fromY) { connectionAngleForText = -connectionAngleForText; connectionAngleY = -connectionAngleY; } var connectorDotOffsetY = Math.tan(connectionAngleY * Math.PI / 180) * componentWidth / 2; connectorDotOffsetY = connectorDotOffsetY < 0 ? Math.max(connectorDotOffsetY, -componentHeight / 2) : Math.min(connectorDotOffsetY, componentHeight / 2); var connectorDotOffsetX = componentHeight / 2 / Math.tan(connectionAngleX * Math.PI / 180); connectorDotOffsetX = connectorDotOffsetX < 0 ? Math.max(connectorDotOffsetX, -componentWidth / 2) : Math.min(connectorDotOffsetX, componentWidth / 2); var connectorDotX = fromX + connectorDotOffsetX; var connectorDotY = fromY + connectorDotOffsetY; /* * All connections are rendered with this component * indexToRender helps determine which connections are made visible, * avoiding blurriness and conflicts when they share a path * */ var indexToRender = 0; if (props.newConnection === false) { toX = toX - connectorDotOffsetX * 1.2; toY = toY - connectorDotOffsetY * 1.2; indexToRender = props.connectionsSharingDirection.length - 1; } else if (props.componentMousedOver !== null && props.componentMousedOver.id !== props.fromComponent.id) { toX = props.componentMousedOver.x - connectorDotOffsetX * 1.2; toY = props.componentMousedOver.y - connectorDotOffsetY * 1.2; } var existingConnection = function (props) { return (createElement(Fragment, null, createElement(Group, { onMouseOver: function (e) { /* this.onHoverChange(true); */ _this.setState({ hovering: true }); e.cancelBubble = true; }, onMouseOut: function (e) { /* this.onHoverChange(false); */ _this.setState({ hovering: false }); e.cancelBubble = true; } }, (createElement(Arrow, { stroke: "grey", strokeWidth: 3, points: [fromX, fromY, toX, toY], tension: 0.4, visible: props.index === indexToRender })), ((props.connectionsSharingPath).map(function (c, i) { var index = i + 1; var extension = _this.getTextExtension(index); return (createElement(Text, { key: c.fromPortId + "-" + c.toComponentId + "-" + i, ref: function (text) { return _this.onText(text, i); }, x: fromX + width / 2, y: fromY + height / 2, offsetY: index % 2 === 1 ? 20 : -10, text: "" + c.port.title.toUpperCase() + extension, rotation: connectionAngleForText, fill: "grey", visible: props.index === indexToRender && /* this.state.hovering === false && */ (Math.abs(width) > componentWidth * 1.7 || Math.abs(height) > componentWidth * 1.7) && // The following check avoids conflicts when we have connections ... // sharing a path but face opposite directions props.fromComponent.id === props.connectionsSharingPath[props.connectionsSharingPath.length - 1].fromComponentId })); })), createElement(Group, { onClick: function () { return props.onClick(props.connection); }, x: fromX + width / 2, y: fromY + height / 2, visible: props.index === indexToRender && _this.state.hovering && // The following check avoids conflicts when we have connections ... // sharing a path but face opposite directions props.fromComponent.id === props.connectionsSharingPath[props.connectionsSharingPath.length - 1].fromComponentId }, createElement(Circle, { fill: "red", radius: 12, shadowBlur: 10, shadowOpacity: 0.5, stroke: "white", strokeWidth: 1 }), _this.state.closeImageLoaded && (createElement(Image$1, { image: _this.closeImage, offsetX: 5, offsetY: 5, width: 10, height: 10 })))), createElement(Group, { x: connectorDotX, y: connectorDotY, visible: props.index === indexToRender }, createElement(Circle, { radius: 14, fill: "#f7f7f7" }), createElement(Circle, { radius: 7, fill: "skyblue" })))); }; var newConnection = function () { return (createElement(Fragment, null, createElement(Arrow, { stroke: "grey", strokeWidth: 3, points: [fromX, fromY, toX, toY], tension: 0.4, ref: _this.onNode, lineCap: "round" }))); }; return (props.newConnection ? newConnection() : existingConnection(props)); }; Connection.prototype.getTextExtension = function (index) { var similarConnectionsExist = false; var props = this.props; props.connectionsSharingDirection.forEach(function (connection) { if (similarConnectionsExist === false) { if (props.connectionsSharingDirection.filter(function (c) { return c.fromPortId === connection.fromPortId; }).length > 1) { similarConnectionsExist = true; } } }); return similarConnectionsExist ? "-" + index : ""; }; return Connection; }(Component)); /** * @class KonvaGraph */ var KonvaGraph = /** @class */ (function (_super) { __extends(KonvaGraph, _super); function KonvaGraph() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.needRAF = false; _this.state = { addingConnection: false, mouseX: -1, mouseY: -1, selectedComponentId: null, componentMousedOver: null, }; _this.keyDown = function (ev) { if (ev.key === "Esc" || ev.key === "Escape" || ev.keyCode === 27) { if (_this.state.addingConnection) { _this.setState({ addingConnection: false }); } else { if (_this.state.selectedComponentId) { _this.props.onComponentSelected(null); _this.setState({ selectedComponentId: null }); } } } }; // handle zoom with wheel _this.onWheel = function (ev) { ev.evt.preventDefault(); var scaleBy = 1.05; var stage = _this.stage; var oldScale = _this.stage.scaleX(); var mousePointTo = { x: stage.getPointerPosition().x / oldScale - stage.x() / oldScale, y: stage.getPointerPosition().y / oldScale - stage.y() / oldScale }; var newScale = ev.evt.deltaY > 0 ? oldScale * scaleBy : oldScale / scaleBy; stage.scale({ x: newScale, y: newScale }); var newPos = { x: -(mousePointTo.x - stage.getPointerPosition().x / newScale) * newScale, y: -(mousePointTo.y - stage.getPointerPosition().y / newScale) * newScale }; stage.position(newPos); stage.batchDraw(); }; _this.getScaledPointerPosition = function () { var pointerPosition = _this.stage.getPointerPosition(); var stageX = _this.stage.getAttr("x"); var stageY = _this.stage.getAttr("y"); var stageScaleX = _this.stage.getAttr("scaleX"); var stageScaleY = _this.stage.getAttr("scaleY"); var x = (pointerPosition.x - stageX) / stageScaleX; var y = (pointerPosition.y - stageY) / stageScaleY; return { x: x, y: y }; }; _this.onMouseMove = function (ev) { if (!_this.stage || _this.needRAF) { return; } _this.needRAF = true; var position = _this.getScaledPointerPosition(); requestAnimationFrame(function () { _this.setState({ // decrease 10 for the arrow to be a little behind the pointer mouseX: position.x - 10, mouseY: position.y }, function () { _this.needRAF = false; }); }); }; _this.onMouseDown = funct