Below is my all code for GoJS component.
export class DiagramEditorComponent implements OnInit {
private diagram: go.Diagram = new go.Diagram();
private palette: go.Palette = new go.Palette();
private nodeObject: go.ObjectData;
private nodeColor: string;
public nodeTextColor: string;
private firstUpdateSkip: number = environment.wireframe_block_Update_Counter;
private selectedNodes = [];
private selectedLinks = [];
public nodeTextSize: number;
public nodeTextStyle: string;
public nodeTextFamily: string = "Helvetica";
public nodeTextFontFamily = ["Helvetica", "sans-serif", "Arial"];
@ViewChild("diagramDiv")
private diagramRef: ElementRef;
@ViewChild("paletteDiv")
private paletteRef: ElementRef;
@ViewChild("nodeColorPicker")
private colorPickerRef: ElementRef;
@ViewChild("nodeTextChange")
private changeNodeTextRef: ElementRef;
// @Input()
// get model(): go.Model { return this.diagram.model; }
// set model(val: go.Model) { this.diagram.model = val; }
@Input() wireframeData: go.Model;
@Input() zIndex: number;
@Input() row: number;
@Input() col: number;
@Input() pointerEvent: boolean;
@Output() onBackgroundClick: EventEmitter<void> = new EventEmitter();
@Output()
nodeSelected = new EventEmitter<go.Node | null>();
@Output()
modelChanged = new EventEmitter<go.Model>();
private nodeBlinkSubscription: Subscription;
private pasteClipWireframeSubscription: Subscription;
private copyPasteWireframeSubscription: Subscription;
constructor(
private modalService: NgbModal,
private commonService: CommonService
) {
const $ = go.GraphObject.make;
// Place GoJS license key here:
(go as any).Diagram.licenseKey = Constants.GOJS_LICENSE_KEY;
this.diagram = new go.Diagram();
this.diagram.initialContentAlignment = go.Spot.TopLeft;
this.diagram.allowDrop = true;
this.diagram.allowVerticalScroll = false;
this.diagram.allowHorizontalScroll = false;
this.diagram.allowZoom = false;
this.diagram.undoManager.isEnabled = true;
this.diagram.toolManager.draggingTool = new GuidedDraggingTool();
this.diagram.toolManager.linkingTool.isUnconnectedLinkValid = true;
this.diagram.toolManager.relinkingTool.isUnconnectedLinkValid = true;
this.diagram.toolManager.draggingTool.dragsLink = true;
this.diagram.animationManager.isEnabled = false;
this.diagram.addDiagramListener("ChangedSelection", (e) => {
const node = e.diagram.selection.first();
this.nodeSelected.emit(node instanceof go.Node ? node : null);
});
this.diagram.addDiagramListener(
"ExternalObjectsDropped",
this.setScreenHeightWidthOnNode
);
this.diagram.addDiagramListener(
"LinkDrawn",
this.setScreenHeightWidthOnNode
);
this.diagram.addDiagramListener(
"SelectionMoved",
this.setScreenHeightWidthOnNode
);
this.diagram.addDiagramListener(
"SelectionCopied",
this.setScreenHeightWidthOnNode
);
this.diagram.addDiagramListener(
"PartResized",
this.setScreenHeightWidthOnNode
);
this.diagram.addDiagramListener(
"ChangedSelection",
this.onItemSelect.bind(this)
);
this.diagram.addDiagramListener(
"BackgroundSingleClicked",
this.onBgClick.bind(this)
);
this.diagram.addModelChangedListener(
(e) => e.isTransactionFinished && this.updateWireframeData(e)
);
let nodeSelectionAdornmentTemplate: go.Adornment = $(
go.Adornment,
"Auto",
$(go.Shape, {
fill: null,
stroke: "deepskyblue",
strokeWidth: 1.5,
strokeDashArray: [4, 2],
}),
$(go.Placeholder)
);
let nodeResizeAdornmentTemplate: go.Adornment = $(
go.Adornment,
"Spot",
{ locationSpot: go.Spot.Right },
$(go.Placeholder),
$(go.Shape, {
alignment: go.Spot.TopLeft,
cursor: "nw-resize",
desiredSize: new go.Size(6, 6),
fill: "lightblue",
stroke: "deepskyblue",
}),
$(go.Shape, {
alignment: go.Spot.Top,
cursor: "n-resize",
desiredSize: new go.Size(6, 6),
fill: "lightblue",
stroke: "deepskyblue",
}),
$(go.Shape, {
alignment: go.Spot.TopRight,
cursor: "ne-resize",
desiredSize: new go.Size(6, 6),
fill: "lightblue",
stroke: "deepskyblue",
}),
$(go.Shape, {
alignment: go.Spot.Left,
cursor: "w-resize",
desiredSize: new go.Size(6, 6),
fill: "lightblue",
stroke: "deepskyblue",
}),
$(go.Shape, {
alignment: go.Spot.Right,
cursor: "e-resize",
desiredSize: new go.Size(6, 6),
fill: "lightblue",
stroke: "deepskyblue",
}),
$(go.Shape, {
alignment: go.Spot.BottomLeft,
cursor: "se-resize",
desiredSize: new go.Size(6, 6),
fill: "lightblue",
stroke: "deepskyblue",
}),
$(go.Shape, {
alignment: go.Spot.Bottom,
cursor: "s-resize",
desiredSize: new go.Size(6, 6),
fill: "lightblue",
stroke: "deepskyblue",
}),
$(go.Shape, {
alignment: go.Spot.BottomRight,
cursor: "sw-resize",
desiredSize: new go.Size(6, 6),
fill: "lightblue",
stroke: "deepskyblue",
})
);
let nodeRotateAdornmentTemplate: go.Adornment = $(
go.Adornment,
{ locationSpot: go.Spot.Center, locationObjectName: "CIRCLE" },
$(go.Shape, "Circle", {
name: "CIRCLE",
cursor: "pointer",
desiredSize: new go.Size(7, 7),
fill: "lightblue",
stroke: "deepskyblue",
}),
$(go.Shape, {
geometryString: "M3.5 7 L3.5 30",
isGeometryPositioned: true,
stroke: "deepskyblue",
strokeWidth: 1.5,
strokeDashArray: [4, 2],
})
);
this.diagram.nodeTemplate = $(
go.Node,
"Spot",
{ locationSpot: go.Spot.Center },
new go.Binding("location", "dloc", go.Point.parse).makeTwoWay(
go.Point.stringify
),
{
selectable: true,
selectionAdornmentTemplate: nodeSelectionAdornmentTemplate,
},
{
resizable: true,
resizeObjectName: "PANEL",
resizeAdornmentTemplate: nodeResizeAdornmentTemplate,
},
{ rotatable: true, rotateAdornmentTemplate: nodeRotateAdornmentTemplate },
new go.Binding("angle").makeTwoWay(),
// the main object is a Panel that surrounds a TextBlock with a Shape
$(
go.Panel,
"Auto",
{ name: "PANEL" },
new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(
go.Size.stringify
),
$(
go.Shape,
"Rectangle", // default figure
{
portId: "", // the default port: if no spot on link data, use closest side
fromLinkable: true,
toLinkable: true,
cursor: "pointer",
fill: "white", // default color
},
new go.Binding("figure"),
new go.Binding("fill")
),
$(
go.TextBlock,
{
font: "bold 11pt Helvetica, Arial, sans-serif",
margin: 8,
//maxSize: new go.Size(160, NaN),
wrap: go.TextBlock.WrapFit,
editable: true,
},
new go.Binding("text").makeTwoWay(),
new go.Binding("stroke"),
new go.Binding("font")
),
{
// define a context menu for each node
contextMenu: $(
go.Adornment,
"Vertical", // that has one button
$("ContextMenuButton", $(go.TextBlock, "Change BG Color"), {
click: this.changeColor.bind(this),
}),
$("ContextMenuButton", $(go.TextBlock, "Change Font"), {
click: this.changeFont.bind(this),
})
// more ContextMenuButtons would go here
), // end Adornment
}
),
// four small named ports, one on each side:
this.makePort("T", go.Spot.Top, false, true),
this.makePort("L", go.Spot.Left, true, true),
this.makePort("R", go.Spot.Right, true, true),
this.makePort("B", go.Spot.Bottom, true, false),
{
// handle mouse enter/leave events to show/hide the ports
mouseEnter: (e, node) => {
this.showSmallPorts(node, true);
},
mouseLeave: (e, node) => {
this.showSmallPorts(node, false);
},
}
);
let linkSelectionAdornmentTemplate: go.Adornment = $(
go.Adornment,
"Link",
$(
go.Shape,
// isPanelMain declares that this Shape shares the Link.geometry
{ isPanelMain: true, fill: null, stroke: "deepskyblue", strokeWidth: 0 }
) // use selection object's strokeWidth
);
this.diagram.linkTemplate = $(
go.Link,
// allow relinking
{
selectable: true,
selectionAdornmentTemplate: linkSelectionAdornmentTemplate,
},
{ relinkableFrom: true, relinkableTo: true, reshapable: true },
{
routing: go.Link.AvoidsNodes,
curve: go.Link.JumpOver,
corner: 5,
toShortLength: 4,
},
new go.Binding("points").makeTwoWay(), // this line needs to commented for responsive links rendering
$(
go.Shape, // the link path shape
{ isPanelMain: true, strokeWidth: 2 }
),
$(
go.Shape, // the arrowhead
{ toArrow: "Standard", stroke: null }
),
$(
go.Panel,
"Auto",
new go.Binding("visible").ofObject(),
$(
go.Shape,
"RoundedRectangle", // the link shape
{ fill: null, stroke: null }
),
$(
go.TextBlock,
{
textAlign: "center",
font: "bold 10pt helvetica, arial, sans-serif",
margin: 2,
minSize: new go.Size(10, NaN),
editable: true,
},
new go.Binding("text").makeTwoWay()
)
)
);
let figures = new Figures();
figures.defineBpmnTaskUserIcon();
figures.defineDatabaseIcon();
figures.defineLinedDocIcon();
figures.defineEthernetIcon();
figures.defineFiveBarUserIcon();
figures.defineWarhouseIcon();
this.palette = new go.Palette();
this.palette.nodeTemplateMap = this.diagram.nodeTemplateMap;
(this.palette.layout = $(go.GridLayout, {
cellSize: new go.Size(1, 1),
spacing: new go.Size(5, 5),
wrappingColumn: 12,
})),
// initialize contents of Palette
(this.palette.model.nodeDataArray = [
{ text: "Org", figure: "Warehouse", fill: "lightgreen" },
{
text: "Role",
figure: "BpmnTaskUser",
fill: "orange"
},
{ text: "Start", figure: "Circle", fill: "green" },
{ text: "Step" },
{ text: "???", figure: "Diamond", fill: "lightskyblue" },
{ text: "Document", figure: "LinedDocument", fill: "pink" },
{ text: "DB", figure: "Database", fill: "lightgray" },
{ text: "System", figure: "Ethernet", fill: "darkgray" },
{ text: "End", figure: "Circle", fill: "red" },
{ text: "Comment", figure: "RoundedRectangle", fill: "lightyellow" },
{ text: "Measure", figure: "5Bars", fill: "pink" },
]);
}
ngOnInit() {
this.diagram.div = this.diagramRef.nativeElement;
//this.palette.div = this.paletteRef.nativeElement;
this.palette.div = <HTMLDivElement>document.getElementById("paletteDiv");
this.nodeBlinkSubscription = this.commonService.blinkWireframeNode.subscribe(
(nodeIndex: number) => {
console.log("wireframe animation");
let i = 0;
let animation = new go.Animation();
this.diagram.nodes.each((node) => {
if (i == nodeIndex) {
animation.add(node, "scale", node.scale, 2);
animation.start();
setTimeout(() => {
let sanimation = new go.Animation();
sanimation.add(node, "scale", node.scale, 1);
sanimation.start();
}, 700);
}
i++;
});
}
);
this.copyPasteWireframeSubscription = this.commonService.onWireframeCopyPasteOperation.subscribe((res) => {
if (res.isPaste) {
if (
!this.commonService.WireframeClipboard ||
!this.commonService.WireframeClipboard.nodes
)
return;
this.commonService.showLoader();
//do paste
if (
!this.commonService.BodyJson.rows[this.row].cols[this.col]
.wireFrameData
) {
//@ts-ignore
this.commonService.BodyJson.rows[this.row].cols[
this.col
//@ts-ignore
].wireFrameData = { nodeDataArray: [], linkDataArray: [] };
}
let wireFrameData = this.commonService.BodyJson.rows[this.row].cols[
this.col
].wireFrameData;
if (wireFrameData && wireFrameData.nodeDataArray) {
wireFrameData.nodeDataArray = wireFrameData.nodeDataArray.concat(
this.commonService.WireframeClipboard.nodes
);
} else {
wireFrameData.nodeDataArray = this.commonService.WireframeClipboard.nodes;
}
if (wireFrameData && wireFrameData["linkDataArray"]) {
wireFrameData["linkDataArray"] = wireFrameData[
"linkDataArray"
].concat(this.commonService.WireframeClipboard.links);
} else {
wireFrameData[
"linkDataArray"
] = this.commonService.WireframeClipboard.links;
}
// for (var i = 0; i < wireFrameData["linkDataArray"].length; i++) {
// if (
// wireFrameData["linkDataArray"][i].from &&
// wireFrameData["linkDataArray"][i].to
// ) {
// delete wireFrameData["linkDataArray"][i].points;
// }
// }
this.diagram.clear();
//this.diagram.model = new go.GraphLinksModel(this.wireframeData.draggableNodeDataArray, this.wireframeData.draggableLinkDataArray);
setTimeout(() => {
this.makeDraggableChart();
this.commonService.hideLoader();
setTimeout(() => {
this.updateWireframeData(null);
}, 2000);
}, 2200);
} else {
//do copy
this.commonService.WireframeClipboard = {
nodes: this.selectedNodes,
links: this.selectedLinks,
};
}
});
this.pasteClipWireframeSubscription = this.commonService.pasteClipboardData.subscribe((res) => {
if (res.isWireframeFound) {
this.diagram.clear();
//this.diagram.model = new go.GraphLinksModel(this.wireframeData.draggableNodeDataArray, this.wireframeData.draggableLinkDataArray);
setTimeout(() => {
this.makeDraggableChart();
this.commonService.hideLoader();
setTimeout(() => {
this.updateWireframeData(null);
}, 2000);
}, 2200);
}
});
}
ngOnChanges(changes: SimpleChange) {
if (changes["row"] || changes["col"]) {
this.diagram.clear();
//this.diagram.model = new go.GraphLinksModel(this.wireframeData.draggableNodeDataArray, this.wireframeData.draggableLinkDataArray);
setTimeout(() => {
this.firstUpdateSkip = environment.wireframe_block_Update_Counter;
this.makeDraggableChart();
}, 2200);
}
setTimeout(() => {
let canvas = this.diagramRef.nativeElement
.children[0] as HTMLCanvasElement;
canvas.style["z-index"] = this.zIndex;
$(".diagramDiv > div").css("z-index", this.zIndex - 1);
if (this.pointerEvent) {
canvas.style["pointer-events"] = "none";
} else {
canvas.style["pointer-events"] = "all";
}
}, 2000);
}
makeDraggableChart() {
//console.log('copied data', this.commonService.WireframeClipboard);
let wireFrameData = this.commonService.BodyJson.rows[this.row].cols[
this.col
].wireFrameData;
console.log('wireFrameData', wireFrameData);
if (wireFrameData && wireFrameData.nodeDataArray) {
for (var i = 0; i < wireFrameData.nodeDataArray.length; i++) {
wireFrameData.nodeDataArray[i].dloc = this.recalculatePositions(
wireFrameData.nodeDataArray[i].screenWidth,
wireFrameData.nodeDataArray[i].screenHeight,
wireFrameData.nodeDataArray[i].dloc
);
if (wireFrameData.nodeDataArray[i].size)
wireFrameData.nodeDataArray[i].size = this.recalculatePositions(
wireFrameData.nodeDataArray[i].screenWidth,
wireFrameData.nodeDataArray[i].screenHeight,
wireFrameData.nodeDataArray[i].size
);
wireFrameData.nodeDataArray[i].screenWidth = document.getElementById(
"assessmentCarousel"
).offsetWidth;
wireFrameData.nodeDataArray[i].screenHeight = document.getElementById(
"assessmentCarousel"
).offsetHeight;
}
if (wireFrameData && wireFrameData["linkDataArray"]) {
for (var i = 0; i < wireFrameData["linkDataArray"].length; i++) {
if (wireFrameData["linkDataArray"][i].points) {
for (var j = 0; j < wireFrameData["linkDataArray"][i].points.length; j++) {
// console.log('points toArray', wireFrameData["linkDataArray"][i].points._dataArray);
wireFrameData["linkDataArray"][i].points[j] = this.recalculateLinkPositions(
wireFrameData["linkDataArray"][i].screenWidth ? wireFrameData["linkDataArray"][i].screenWidth : null,
wireFrameData["linkDataArray"][i].screenHeight ? wireFrameData["linkDataArray"][i].screenHeight : null,
wireFrameData["linkDataArray"][i].points[j],
j
);
}
wireFrameData["linkDataArray"][i].screenWidth = document.getElementById(
"assessmentCarousel"
).offsetWidth;
wireFrameData["linkDataArray"][i].screenHeight = document.getElementById(
"assessmentCarousel"
).offsetHeight;
}
}
}
this.diagram.model = go.Model.fromJson(wireFrameData);
} else {
this.diagram.model = new go.GraphLinksModel();
}
let pos = this.diagram.model.modelData.position;
if (pos) {
let newPos = this.recalculatePositions(
this.diagram.model.modelData.screenWidth,
this.diagram.model.modelData.screenHeight,
this.diagram.model.modelData.position
);
this.diagram.initialPosition = go.Point.parse(newPos);
}
}
updateWireframeData(e: any) {
if (this.firstUpdateSkip == 0) {
// console.log(e, e.model.nodeDataArray);
// this.wireframeData.draggableNodeDataArray = e.model.nodeDataArray;
// this.wireframeData.draggableLinkDataArray = e.model.linkDataArray;
this.diagram.model.modelData = {
position: go.Point.stringify(this.diagram.position),
screenWidth: document.getElementById("assessmentCarousel").offsetWidth,
screenHeight: document.getElementById("assessmentCarousel")
.offsetHeight,
modifiedBy: window["_spPageContextInfo"]
? window["_spPageContextInfo"].userDisplayName
: "",
modifiedDate: new Date().toISOString(),
};
let wireframe = JSON.parse(this.diagram.model.toJson());
// for (var i = 0; i < wireframe.linkDataArray.length; i++) {
// if (wireframe.linkDataArray[i].from && wireframe.linkDataArray[i].to) {
// // delete wireframe.linkDataArray[i].points;
// }
// }
this.commonService.BodyJson.rows[this.row].cols[
this.col
].wireFrameData = wireframe;
console.log(
"updated wireframe",
this.commonService.BodyJson.rows[this.row].cols[this.col].wireFrameData
);
// this.modelChanged.emit(this.wireframeData);
} else {
this.firstUpdateSkip--;
}
}
setScreenHeightWidthOnNode(e: go.DiagramEvent) {
const obj = e.diagram.selection.first();
if (obj instanceof go.Node) {
obj.data["screenWidth"] = document.getElementById(
"assessmentCarousel"
).offsetWidth;
obj.data["screenHeight"] = document.getElementById(
"assessmentCarousel"
).offsetHeight;
} else if (obj instanceof go.Link) {
obj.data["screenWidth"] = document.getElementById(
"assessmentCarousel"
).offsetWidth;
obj.data["screenHeight"] = document.getElementById(
"assessmentCarousel"
).offsetHeight;
}
}
onBgClick() {
this.onBackgroundClick.emit();
}
recalculatePositions(width, height, pos) {
let oldWidth = width ? width : 1477;
let oldHeight = height ? height : 727;
let currentWidth = document.getElementById("assessmentCarousel")
.offsetWidth;
let currentHeight = document.getElementById("assessmentCarousel")
.offsetHeight;
let oldPosX = parseFloat(pos.split(" ")[0]);
let oldPosY = parseFloat(pos.split(" ")[1]);
let newPosX = (currentWidth * oldPosX) / oldWidth;
let newPosY = (currentHeight * oldPosY) / oldHeight;
return newPosX + " " + newPosY;
}
recalculateLinkPositions(width, height, pos, axis) {
let oldWidth = width ? width : 1477;
let oldHeight = height ? height : 727;
let currentWidth = document.getElementById("assessmentCarousel")
.offsetWidth;
let currentHeight = document.getElementById("assessmentCarousel")
.offsetHeight;
if (axis % 2 == 0) { // x axis
let oldPosX = parseFloat(pos);
let newPosX = (currentWidth * oldPosX) / oldWidth;
return newPosX;
}
else { // y axis
let oldPosY = parseFloat(pos);
let newPosY = (currentHeight * oldPosY) / oldHeight;
return newPosY;
}
}
makePort(name, spot, output, input) {
const $ = go.GraphObject.make;
// the port is basically just a small transparent square
return $(go.Shape, "Circle", {
fill: null, // not seen, by default; set to a translucent gray by showSmallPorts, defined below
stroke: null,
desiredSize: new go.Size(7, 7),
alignment: spot, // align the port on the main Shape
alignmentFocus: spot, // just inside the Shape
portId: name, // declare this object to be a "port"
fromSpot: spot,
toSpot: spot, // declare where links may connect at this port
fromLinkable: output,
toLinkable: input, // declare whether the user may draw links to/from here
cursor: "pointer", // show a different cursor to indicate potential link point
});
}
showSmallPorts(node, show) {
node.ports.each((port) => {
if (port.portId !== "") {
// don't change the default port, which is the big shape
port.fill = show ? "rgba(0,0,0,.3)" : null;
}
});
}
changeColor(e, obj) {
let contextMenu = obj.part;
this.nodeObject = contextMenu.data;
console.log("curent color", this.nodeColor);
const modalRef = this.modalService.open(this.colorPickerRef);
modalRef.result
.then((res) => {
if (res == "success") {
this.diagram.model.startTransaction("changed color");
this.diagram.model.setDataProperty(
this.nodeObject,
"fill",
this.nodeColor
);
this.diagram.model.commitTransaction("changed color");
}
})
.catch((err) => {
console.log(err);
});
}
changeFont(e, obj) {
let contextMenu = obj.part;
this.nodeObject = contextMenu.data;
console.log("curent node text color", this.nodeTextColor);
console.log("this.nodeObject", this.nodeObject);
if (this.nodeObject.font) {
let splitArr = this.nodeObject.font.split(" ");
console.log("splitArr", splitArr);
this.nodeTextSize = splitArr[1].split("pt")[0];
this.nodeTextStyle = splitArr[0];
this.nodeTextFamily = splitArr[2];
} else {
this.nodeTextSize = 11;
this.nodeTextStyle = "bold";
this.nodeTextFamily = "Helvetica";
}
if (this.nodeObject.stroke) {
this.nodeTextColor = this.nodeObject.stroke;
} else {
this.nodeTextColor = "#000000";
}
const modalRef = this.modalService.open(this.changeNodeTextRef);
modalRef.result
.then((res) => {
if (res == "success") {
let fontObj: string =
this.nodeTextStyle +
" " +
this.nodeTextSize +
"pt" +
" " +
this.nodeTextFamily;
console.log("fontObj", fontObj);
this.diagram.model.startTransaction("changed node text");
this.diagram.model.setDataProperty(
this.nodeObject,
"stroke",
this.nodeTextColor
);
this.diagram.model.setDataProperty(this.nodeObject, "font", fontObj);
this.diagram.model.commitTransaction("changed node text");
}
})
.catch((err) => {
console.log(err);
});
}
onItemSelect(e: any) {
console.log("selected", e.subject);
this.selectedNodes = [];
this.selectedLinks = [];
var it = e.subject.iterator;
while (it.next()) {
var part = it.value;
if (part instanceof go.Node) {
this.selectedNodes.push(part.data);
} else if (part instanceof go.Link) {
this.selectedLinks.push(part.data);
}
}
console.log("selected nodes", this.selectedNodes);
console.log("selected links", this.selectedLinks);
}
ngOnDestroy() {
this.nodeBlinkSubscription.unsubscribe();
this.pasteClipWireframeSubscription.unsubscribe();
this.copyPasteWireframeSubscription.unsubscribe();
}
}