419 lines
13 KiB
TypeScript
419 lines
13 KiB
TypeScript
import go from "gojs";
|
|
import { parse } from "mathjs";
|
|
import { copy, SPLITCHAR } from "../../../../lib/helpers/objectMethods";
|
|
import { replaceAll } from "../../../../lib/helpers/stringMethods";
|
|
|
|
|
|
function analyseEquation(equation: string) {
|
|
|
|
let hasOperators = false;
|
|
let hasConstants = false;
|
|
let varsRoot = new Array<string>();
|
|
const vars = new Array<string>();
|
|
|
|
/** Fill the List => with all vars and select only the Single Elements: */
|
|
parse(equation).filter(function (node) {
|
|
if (node.isOperatorNode) {
|
|
hasOperators = true;
|
|
}
|
|
if (node.isConstantNode) {
|
|
hasConstants = true;
|
|
}
|
|
return node.isSymbolNode;
|
|
}).forEach(function (node) {
|
|
/** Store the Name */
|
|
varsRoot.push(node.name);
|
|
});
|
|
|
|
// Extract all contained Vars.
|
|
parse(replaceAll(equation, SPLITCHAR, "_SPLITCHAR_")).filter(function (node) {
|
|
return node.isSymbolNode;
|
|
}).forEach(function (node) {
|
|
/** Store the Name */
|
|
vars.push(replaceAll(node.name, "_SPLITCHAR_", SPLITCHAR));
|
|
});
|
|
|
|
varsRoot = Array.from(new Set<string>(varsRoot));
|
|
|
|
return {
|
|
hasConstants,
|
|
hasOperators,
|
|
varsRoot,
|
|
vars
|
|
};
|
|
|
|
}
|
|
|
|
export function transitionTemplate(selctionExtension = []): go.Node {
|
|
const make = go.GraphObject.make; // for conciseness in defining templates
|
|
|
|
// some constants that will be reused within templates
|
|
const mtb8 = new go.Margin(8, 0, 8, 0);
|
|
const mr8 = new go.Margin(0, 8, 0, 0);
|
|
const ml8 = new go.Margin(0, 0, 0, 8);
|
|
const mrl8 = new go.Margin(0, 8, 0, 8);
|
|
|
|
const roundedRectangleParams = {
|
|
parameter1: 2, // set the rounded corner
|
|
spot1: go.Spot.TopLeft,
|
|
spot2: go.Spot.BottomRight // make content go all the way to inside edges of rounded corners
|
|
};
|
|
|
|
// define Converters to be used for Bindings
|
|
function iconConverter(value) {
|
|
return value;
|
|
}
|
|
|
|
// Create a Vertical Panel.
|
|
function makeVerticalPanel(
|
|
binding: string,
|
|
side: "left" | "right",
|
|
portSize = new go.Size(8, 8)
|
|
) {
|
|
let order: any = [];
|
|
if (side === "left") {
|
|
order = [
|
|
make(
|
|
go.Shape,
|
|
"RoundedRectangle",
|
|
{
|
|
stroke: null,
|
|
strokeWidth: 0,
|
|
desiredSize: portSize,
|
|
margin: new go.Margin(1, 0)
|
|
},
|
|
new go.Binding("fill", "portColor")
|
|
),
|
|
make(
|
|
go.TextBlock,
|
|
{
|
|
row: 0,
|
|
alignment: go.Spot.Left,
|
|
font: "16px Roboto, sans-serif",
|
|
stroke: "rgba(0, 0, 0, .87)",
|
|
margin: mrl8
|
|
},
|
|
new go.Binding("text", "label")
|
|
)
|
|
];
|
|
} else {
|
|
order = [
|
|
make(
|
|
go.TextBlock,
|
|
{
|
|
row: 0,
|
|
alignment: go.Spot.Left,
|
|
font: "16px Roboto, sans-serif",
|
|
stroke: "rgba(0, 0, 0, .87)",
|
|
maxSize: new go.Size(160, NaN),
|
|
margin: mrl8
|
|
},
|
|
new go.Binding("text", "label")
|
|
),
|
|
make(
|
|
go.Shape,
|
|
"RoundedRectangle",
|
|
{
|
|
stroke: null,
|
|
strokeWidth: 0,
|
|
desiredSize: portSize,
|
|
margin: new go.Margin(1, 0)
|
|
},
|
|
new go.Binding("fill", "portColor")
|
|
)
|
|
];
|
|
}
|
|
|
|
return make(go.Panel, "Vertical", new go.Binding("itemArray", binding), {
|
|
alignment: side === "left" ? go.Spot.Left : go.Spot.Right,
|
|
itemTemplate: make(
|
|
go.Panel,
|
|
"Horizontal",
|
|
Object.assign({
|
|
_side: side, // internal property to make it easier to tell which side it's on
|
|
fromLinkable: side !== "left",
|
|
toLinkable: side === "left",
|
|
cursor: "pointer",
|
|
alignment: side === "left" ? go.Spot.Left : go.Spot.Right,
|
|
mouseEnter: function (e, port) {
|
|
// the PORT argument will be this Shape
|
|
if (!e.diagram.isReadOnly) {
|
|
port.fill = "rgba(255,0,255,0.5)";
|
|
e.diagram.linkTemplate = e.diagram.linkTemplateMap.get("dataFlow");
|
|
e.diagram.toolManager.linkingTool.archetypeLinkData = {
|
|
category: "dataFlow"
|
|
};
|
|
}
|
|
},
|
|
}, side !== "left" ? { fromSpot: go.Spot.RightSide } : { toSpot: go.Spot.Left })
|
|
,
|
|
new go.Binding("portId", "portId"),
|
|
...order
|
|
) // end itemTemplate
|
|
}); // end Vertical Panel
|
|
}
|
|
|
|
// This function provides a common style for most of the TextBlocks.
|
|
// Some of these values may be overridden in a particular TextBlock.
|
|
function textStyle(field) {
|
|
return [
|
|
{
|
|
font: "12px Roboto, sans-serif",
|
|
stroke: "rgba(0, 0, 0, .60)",
|
|
visible: false // only show textblocks when there is corresponding data for them
|
|
},
|
|
new go.Binding("visible", field, function (val) {
|
|
return val !== undefined;
|
|
})
|
|
];
|
|
}
|
|
|
|
// Define a function for creating a "port" that is normally transparent.
|
|
// The "name" is used as the GraphObject.portId,
|
|
// the "align" is used to determine where to position the port relative to the body of the node,
|
|
// the "spot" is used to control how links connect with the port and whether the port
|
|
// stretches along the side of the node,
|
|
// and the boolean "output" and "input" arguments control whether the user can draw links from or to the port.
|
|
function makePort(name, align, spot, output, input) {
|
|
const horizontal =
|
|
align.equals(go.Spot.Top) || align.equals(go.Spot.Bottom);
|
|
// the port is basically just a transparent rectangle that stretches along the side of the node,
|
|
// and becomes colored when the mouse passes over it
|
|
return make(go.Shape, Object.assign({
|
|
fill: "transparent", // changed to a color in the mouseEnter event handler
|
|
strokeWidth: 0, // no stroke
|
|
width: horizontal ? NaN : 8, // if not stretching horizontally, just 8 wide
|
|
height: !horizontal ? NaN : 8, // if not stretching vertically, just 8 tall
|
|
alignment: align, // align the port on the main Shape
|
|
stretch: horizontal ? go.GraphObject.Horizontal : go.GraphObject.Vertical,
|
|
portId: name, // declare this object to be a "port"
|
|
// fromSpot: spot, // declare where links may connect at this port
|
|
fromLinkable: output, // declare whether the user may draw links from here
|
|
// toSpot: spot, // declare where links may connect at this port
|
|
toLinkable: input, // declare whether the user may draw links to here
|
|
cursor: "pointer", // show a different cursor to indicate potential link point
|
|
mouseEnter: function (e, port) {
|
|
// the PORT argument will be this Shape
|
|
if (!e.diagram.isReadOnly) {
|
|
port.fill = "rgba(255,0,255,0.5)";
|
|
e.diagram.linkTemplate = e.diagram.linkTemplateMap.get("logicFlow");
|
|
e.diagram.toolManager.linkingTool.archetypeLinkData = {
|
|
category: "logicFlow"
|
|
};
|
|
}
|
|
|
|
},
|
|
mouseLeave: function (e, port) {
|
|
port.fill = "transparent";
|
|
}
|
|
}, output ? {
|
|
fromSpot: spot, // declare where links may connect at this port
|
|
} : {}, input ? {
|
|
toSpot: spot, // declare where links may connect at this port
|
|
} : {}));
|
|
}
|
|
|
|
return make(
|
|
go.Node,
|
|
"Auto",
|
|
{
|
|
locationSpot: go.Spot.Top,
|
|
isShadowed: true,
|
|
shadowBlur: 1,
|
|
shadowOffset: new go.Point(0, 1),
|
|
shadowColor: "rgba(0, 0, 0, .14)",
|
|
// selection adornment to match shape of nodes
|
|
selectionAdornmentTemplate: make(
|
|
go.Adornment,
|
|
"Auto",
|
|
make(go.Shape, "RoundedRectangle", roundedRectangleParams, {
|
|
fill: null,
|
|
stroke: "#7986cb",
|
|
strokeWidth: 3
|
|
}),
|
|
make(go.Placeholder),
|
|
...selctionExtension
|
|
|
|
) // end Adornment
|
|
},
|
|
make(
|
|
go.Shape,
|
|
"RoundedRectangle",
|
|
roundedRectangleParams,
|
|
{ name: "SHAPE", fill: "#ffffff", strokeWidth: 1 },
|
|
// gold if highlighted, white otherwise
|
|
new go.Binding("fill", "isHighlighted", function (h) {
|
|
return h ? "gold" : "#ffffff";
|
|
}).ofObject()
|
|
),
|
|
make(
|
|
go.Panel,
|
|
"Vertical",
|
|
{
|
|
stretch: go.GraphObject.Fill
|
|
},
|
|
make(
|
|
go.Panel,
|
|
"Horizontal",
|
|
{ margin: 8 },
|
|
make(
|
|
go.Picture, // flag image, only visible if a nation is specified
|
|
{ margin: mr8, visible: false, desiredSize: new go.Size(50, 50) },
|
|
new go.Binding("source", "icon", iconConverter),
|
|
new go.Binding("visible", "icon", function (icon) {
|
|
return icon !== undefined;
|
|
})
|
|
),
|
|
make(
|
|
go.Panel,
|
|
"Table",
|
|
{
|
|
stretch: go.GraphObject.Horizontal
|
|
},
|
|
make(
|
|
go.TextBlock,
|
|
{
|
|
row: 0,
|
|
alignment: go.Spot.Left,
|
|
font: "16px Roboto, sans-serif",
|
|
stroke: "rgba(0, 0, 0, .87)",
|
|
editable: true
|
|
},
|
|
new go.Binding("text", "label")
|
|
),
|
|
make(
|
|
go.TextBlock,
|
|
textStyle("description"),
|
|
{
|
|
row: 1,
|
|
alignment: go.Spot.Left,
|
|
editable: true,
|
|
isMultiline: true
|
|
},
|
|
new go.Binding("text", "description")
|
|
),
|
|
make("PanelExpanderButton", "GUARD", {
|
|
row: 0,
|
|
column: 1,
|
|
rowSpan: 2,
|
|
margin: ml8
|
|
}),
|
|
// Expander for the Service
|
|
make("PanelExpanderButton", "SERVICE", {
|
|
row: 1,
|
|
column: 1,
|
|
rowSpan: 2,
|
|
margin: ml8
|
|
})
|
|
)
|
|
),
|
|
// Horizontal Lane as devider:
|
|
make(
|
|
go.Shape,
|
|
"LineH",
|
|
{
|
|
stroke: "rgba(0, 0, 0, .60)",
|
|
strokeWidth: 1,
|
|
height: 1,
|
|
stretch: go.GraphObject.Horizontal
|
|
},
|
|
new go.Binding("visible", "guardVisible").ofObject("GUARD").makeTwoWay() // only visible when info is expanded
|
|
),
|
|
// Vertiacal Layout Holding the Guard
|
|
make(
|
|
go.Panel,
|
|
"Auto",
|
|
{
|
|
margin: mtb8,
|
|
stretch: go.GraphObject.Fill,
|
|
name: "GUARD"
|
|
},
|
|
makeVerticalPanel("guardInputs", "left"),
|
|
make(
|
|
go.TextBlock,
|
|
{
|
|
row: 0,
|
|
alignment: go.Spot.Center,
|
|
font: "16px Roboto, sans-serif",
|
|
stroke: "rgba(0, 0, 0, .87)",
|
|
margin: mrl8,
|
|
editable: true,
|
|
textValidation: (textblock: go.TextBlock, oldText, newText) => {
|
|
const node = textblock.part;
|
|
|
|
// Perform a Test.
|
|
try {
|
|
const result = analyseEquation(newText);
|
|
|
|
// Now that we know which variable is contained,
|
|
// we try to creat them.
|
|
|
|
setTimeout(() => {
|
|
node.diagram.startTransaction("add_ports");
|
|
node.data.guardInputs = result.vars.map(label => {
|
|
return {
|
|
label,
|
|
partId: "guard." + label
|
|
};
|
|
});
|
|
node.data.guardOutputs = [
|
|
{
|
|
portId: "guard.result",
|
|
label: "result",
|
|
},
|
|
];
|
|
// Assign the New Text
|
|
node.data.guard = newText;
|
|
// Assing a copy to use this element
|
|
node.data = copy(node.data);
|
|
node.diagram.commitTransaction("add_ports");
|
|
|
|
console.log("data", node.data);
|
|
}, 10);
|
|
return true;
|
|
} catch (e) {
|
|
// The Equation is wrong, give a hint
|
|
return false;
|
|
}
|
|
}
|
|
},
|
|
new go.Binding("text", "guard")
|
|
),
|
|
makeVerticalPanel("guardOutputs", "right")
|
|
),
|
|
make(
|
|
go.Shape,
|
|
"LineH",
|
|
{
|
|
stroke: "rgba(0, 0, 0, .60)",
|
|
strokeWidth: 1,
|
|
height: 1,
|
|
stretch: go.GraphObject.Horizontal
|
|
},
|
|
new go.Binding("visible").ofObject("SERVICE") // only visible when info is expanded
|
|
),
|
|
|
|
make(
|
|
go.Panel,
|
|
"Auto",
|
|
{ margin: mtb8, stretch: go.GraphObject.Fill, name: "SERVICE" },
|
|
makeVerticalPanel("serviceInputs", "left"),
|
|
make(
|
|
go.TextBlock,
|
|
{
|
|
row: 0,
|
|
alignment: go.Spot.Center,
|
|
font: "16px Roboto, sans-serif",
|
|
stroke: "rgba(0, 0, 0, .87)",
|
|
margin: mrl8
|
|
},
|
|
new go.Binding("text", "serviceName")
|
|
),
|
|
makeVerticalPanel("serviceOutputs", "right")
|
|
)
|
|
),
|
|
makePort("LogicFlowIn", go.Spot.Top, go.Spot.Top, false, true),
|
|
makePort("LogicFlowOut", go.Spot.Bottom, go.Spot.Bottom, true, false)
|
|
);
|
|
}
|