flowappplatform-appbuilder-graphs
Version:
Graph Module for Flow App Builder
1,224 lines (1,211 loc) • 58.4 kB
JavaScript
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