nope/resources/ui/gojs/nodes/transition.ts
Martin Karkowski eba29dc9f8 Fixing ui
2021-07-30 07:50:02 +02:00

422 lines
13 KiB
TypeScript

import go from "gojs";
import { parse } from "mathjs";
import { 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", "showDetails")
),
// 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,
maxLines: 1,
textEdited: (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.
node.diagram.startTransaction("add_ports");
node.diagram.model.setDataProperty(node.data, "guardInputs", result.vars.map(label => {
return {
label,
portId: "guard." + label
};
}));
node.diagram.model.setDataProperty(node.data, "guard", newText);
node.diagram.commitTransaction("add_ports");
} catch (e) {
// The Equation is wrong, give a hint
return false;
}
},
textValidation: (textblock: go.TextBlock, oldText, newText) => {
const node = textblock.part;
// Perform a Test.
try {
const result = analyseEquation(newText);
return true;
} catch (e) {
// The Equation is wrong, give a hint
return false;
}
}
},
new go.Binding("text", "guard")
),
makeVerticalPanel("guardOutputs", "right"),
new go.Binding("visible", "showDetails")
),
make(
go.Shape,
"LineH",
{
stroke: "rgba(0, 0, 0, .60)",
strokeWidth: 1,
height: 1,
stretch: go.GraphObject.Horizontal
},
new go.Binding("visible", "showDetails")
),
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"),
new go.Binding("visible", "showDetails")
)
),
makePort("LogicFlowIn", go.Spot.Top, go.Spot.Top, false, true),
makePort("LogicFlowOut", go.Spot.Bottom, go.Spot.Bottom, true, false),
);
}