nope/resources/ui/gojs/nodes/transition.ts

419 lines
13 KiB
TypeScript
Raw Normal View History

2020-12-01 12:05:35 +00:00
import go from "gojs";
2021-07-27 10:55:15 +00:00
import { parse } from "mathjs";
import { copy, SPLITCHAR } from "../../../../lib/helpers/objectMethods";
import { replaceAll } from "../../../../lib/helpers/stringMethods";
2020-12-01 12:05:35 +00:00
2021-07-27 10:55:15 +00:00
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
2020-12-01 12:05:35 +00:00
// 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 = [
2021-07-27 10:55:15 +00:00
make(
2020-12-01 12:05:35 +00:00
go.Shape,
"RoundedRectangle",
{
stroke: null,
strokeWidth: 0,
desiredSize: portSize,
margin: new go.Margin(1, 0)
},
new go.Binding("fill", "portColor")
),
2021-07-27 10:55:15 +00:00
make(
2020-12-01 12:05:35 +00:00
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 = [
2021-07-27 10:55:15 +00:00
make(
2020-12-01 12:05:35 +00:00
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")
),
2021-07-27 10:55:15 +00:00
make(
2020-12-01 12:05:35 +00:00
go.Shape,
"RoundedRectangle",
{
stroke: null,
strokeWidth: 0,
desiredSize: portSize,
margin: new go.Margin(1, 0)
},
new go.Binding("fill", "portColor")
)
];
}
2021-07-27 10:55:15 +00:00
return make(go.Panel, "Vertical", new go.Binding("itemArray", binding), {
2020-12-01 12:05:35 +00:00
alignment: side === "left" ? go.Spot.Left : go.Spot.Right,
2021-07-27 10:55:15 +00:00
itemTemplate: make(
2020-12-01 12:05:35 +00:00
go.Panel,
"Horizontal",
2021-07-27 10:55:15 +00:00
Object.assign({
2020-12-01 12:05:35 +00:00
_side: side, // internal property to make it easier to tell which side it's on
fromLinkable: side !== "left",
toLinkable: side === "left",
cursor: "pointer",
2021-07-27 10:55:15 +00:00
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 })
,
2020-12-01 12:05:35 +00:00
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
2021-07-27 10:55:15 +00:00
return make(go.Shape, Object.assign({
2020-12-01 12:05:35 +00:00
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"
2021-07-27 10:55:15 +00:00
// fromSpot: spot, // declare where links may connect at this port
2020-12-01 12:05:35 +00:00
fromLinkable: output, // declare whether the user may draw links from here
2021-07-27 10:55:15 +00:00
// toSpot: spot, // declare where links may connect at this port
2020-12-01 12:05:35 +00:00
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
2021-07-27 10:55:15 +00:00
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"
};
}
2020-12-01 12:05:35 +00:00
},
mouseLeave: function (e, port) {
port.fill = "transparent";
}
2021-07-27 10:55:15 +00:00
}, output ? {
fromSpot: spot, // declare where links may connect at this port
} : {}, input ? {
toSpot: spot, // declare where links may connect at this port
} : {}));
2020-12-01 12:05:35 +00:00
}
2021-07-27 10:55:15 +00:00
return make(
2020-12-01 12:05:35 +00:00
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
2021-07-27 10:55:15 +00:00
selectionAdornmentTemplate: make(
2020-12-01 12:05:35 +00:00
go.Adornment,
"Auto",
2021-07-27 10:55:15 +00:00
make(go.Shape, "RoundedRectangle", roundedRectangleParams, {
2020-12-01 12:05:35 +00:00
fill: null,
stroke: "#7986cb",
strokeWidth: 3
}),
2021-07-27 10:55:15 +00:00
make(go.Placeholder),
...selctionExtension
2020-12-01 12:05:35 +00:00
) // end Adornment
},
2021-07-27 10:55:15 +00:00
make(
2020-12-01 12:05:35 +00:00
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()
),
2021-07-27 10:55:15 +00:00
make(
2020-12-01 12:05:35 +00:00
go.Panel,
"Vertical",
{
stretch: go.GraphObject.Fill
},
2021-07-27 10:55:15 +00:00
make(
2020-12-01 12:05:35 +00:00
go.Panel,
"Horizontal",
{ margin: 8 },
2021-07-27 10:55:15 +00:00
make(
2020-12-01 12:05:35 +00:00
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),
2021-07-27 10:55:15 +00:00
new go.Binding("visible", "icon", function (icon) {
return icon !== undefined;
2020-12-01 12:05:35 +00:00
})
),
2021-07-27 10:55:15 +00:00
make(
2020-12-01 12:05:35 +00:00
go.Panel,
"Table",
{
stretch: go.GraphObject.Horizontal
},
2021-07-27 10:55:15 +00:00
make(
2020-12-01 12:05:35 +00:00
go.TextBlock,
{
row: 0,
alignment: go.Spot.Left,
font: "16px Roboto, sans-serif",
2021-07-27 10:55:15 +00:00
stroke: "rgba(0, 0, 0, .87)",
editable: true
2020-12-01 12:05:35 +00:00
},
new go.Binding("text", "label")
),
2021-07-27 10:55:15 +00:00
make(
2020-12-01 12:05:35 +00:00
go.TextBlock,
textStyle("description"),
{
row: 1,
2021-07-27 10:55:15 +00:00
alignment: go.Spot.Left,
editable: true,
isMultiline: true
2020-12-01 12:05:35 +00:00
},
new go.Binding("text", "description")
),
2021-07-27 10:55:15 +00:00
make("PanelExpanderButton", "GUARD", {
2020-12-01 12:05:35 +00:00
row: 0,
column: 1,
rowSpan: 2,
margin: ml8
}),
2021-07-27 10:55:15 +00:00
// Expander for the Service
make("PanelExpanderButton", "SERVICE", {
2020-12-01 12:05:35 +00:00
row: 1,
column: 1,
rowSpan: 2,
margin: ml8
})
)
),
// Horizontal Lane as devider:
2021-07-27 10:55:15 +00:00
make(
2020-12-01 12:05:35 +00:00
go.Shape,
"LineH",
{
stroke: "rgba(0, 0, 0, .60)",
strokeWidth: 1,
height: 1,
stretch: go.GraphObject.Horizontal
},
2021-07-27 10:55:15 +00:00
new go.Binding("visible", "guardVisible").ofObject("GUARD").makeTwoWay() // only visible when info is expanded
2020-12-01 12:05:35 +00:00
),
// Vertiacal Layout Holding the Guard
2021-07-27 10:55:15 +00:00
make(
2020-12-01 12:05:35 +00:00
go.Panel,
"Auto",
{
margin: mtb8,
stretch: go.GraphObject.Fill,
name: "GUARD"
},
makeVerticalPanel("guardInputs", "left"),
2021-07-27 10:55:15 +00:00
make(
2020-12-01 12:05:35 +00:00
go.TextBlock,
{
row: 0,
alignment: go.Spot.Center,
font: "16px Roboto, sans-serif",
stroke: "rgba(0, 0, 0, .87)",
2021-07-27 10:55:15 +00:00
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;
}
}
2020-12-01 12:05:35 +00:00
},
new go.Binding("text", "guard")
),
makeVerticalPanel("guardOutputs", "right")
),
2021-07-27 10:55:15 +00:00
make(
2020-12-01 12:05:35 +00:00
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
),
2021-07-27 10:55:15 +00:00
make(
2020-12-01 12:05:35 +00:00
go.Panel,
"Auto",
{ margin: mtb8, stretch: go.GraphObject.Fill, name: "SERVICE" },
makeVerticalPanel("serviceInputs", "left"),
2021-07-27 10:55:15 +00:00
make(
2020-12-01 12:05:35 +00:00
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")
)
),
2021-07-27 10:55:15 +00:00
makePort("LogicFlowIn", go.Spot.Top, go.Spot.Top, false, true),
makePort("LogicFlowOut", go.Spot.Bottom, go.Spot.Bottom, true, false)
2020-12-01 12:05:35 +00:00
);
}