I’m trying to reduce opacity of not selected steps to draw attention to the selected one.
The only problem I face — not consistent border and background colors when applying opacity. Both of them have the same color “#FF5252”, however when I change opacity they look differently, the border is brighter.
Here is the code I’m using
const $ = go.GraphObject.make
const diagram = new go.Diagram(diagramRef.current, {
"undoManager.isEnabled": true,
model: new go.GraphLinksModel({
linkKeyProperty: "key",
nodeDataArray: [
{ key: 1, text: "Start", color: "#B2F5EA", category: "Start" },
{ key: 2, text: "Process 1", color: "#E9D8FD" },
{ key: 3, text: "Decision", color: "#FED7D7", category: "Decision" },
{ key: 4, text: "Process 2", color: "#E9D8FD" },
{ key: 5, text: "Process 3", color: "#E9D8FD" },
{ key: 6, text: "End", color: "#FEB2B2", category: "End" },
],
linkDataArray: [
{ key: -1, from: 1, to: 2 },
{ key: -2, from: 2, to: 3 },
{ key: -3, from: 3, to: 4, text: "Yes" },
{ key: -4, from: 3, to: 5, text: "No" },
{ key: -5, from: 4, to: 6 },
{ key: -6, from: 5, to: 6 },
],
}),
// Enable panning (similar to Hand Tool in Figma)
allowHorizontalScroll: true,
allowVerticalScroll: true,
scrollMode: go.Diagram.InfiniteScroll,
// Enable node dragging
allowMove: true,
allowDrop: true,
// Set initial scale and position
initialContentAlignment: go.Spot.Center,
"animationManager.isEnabled": false,
// Add grid background
grid: $(
go.Panel,
"Grid",
{ gridCellSize: new go.Size(20, 20) },
$(go.Shape, "LineH", { stroke: "lightgray", strokeWidth: 0.5 }),
$(go.Shape, "LineV", { stroke: "lightgray", strokeWidth: 0.5 }),
),
})
// Enable panning tool explicitly after diagram creation
diagram.toolManager.panningTool.isEnabled = true
diagram.toolManager.draggingTool.isEnabled = true
// Also, let's make sure the diagram has the right settings for dragging
diagram.allowDrag = true
// Define node templates
diagram.nodeTemplateMap.add(
"",
$(
go.Node,
"Auto",
{
width: 120,
height: 60,
selectionAdorned: true,
resizable: true,
resizeObjectName: "SHAPE",
// Add shadow effect
shadowVisible: true,
shadowOffset: new go.Point(3, 3),
shadowBlur: 5,
movable: true, // Explicitly set movable to true
opacity: 1, // Default opacity
},
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
$(
go.Shape,
"Rectangle",
{
name: "SHAPE",
fill: "white",
stroke: "#333333",
strokeWidth: 1,
portId: "",
fromLinkable: true,
toLinkable: true,
cursor: "pointer",
},
new go.Binding("fill", "color"),
),
$(
go.TextBlock,
{
margin: 8,
font: "14px sans-serif",
editable: true,
},
new go.Binding("text").makeTwoWay(),
),
),
)
// Create a tooltip for the validation icon
function createValidationTooltip() {
return $(
go.Adornment,
"Auto",
$(go.Shape, "RoundedRectangle", { fill: "#FFFFCC", stroke: "#999999" }),
$(go.TextBlock, { margin: 5, text: "Validation warning: This step requires attention" }),
)
}
// Start node template with validation icon
diagram.nodeTemplateMap.add(
"Start",
$(
go.Node,
"Spot",
{
movable: true, // Explicitly set movable to true
selectionAdorned: true,
opacity: 1, // Default opacity,
resizeObjectName: "MAIN_SHAPE",
selectionObjectName: "MAIN_SHAPE",
locationObjectName: "MAIN_SHAPE",
},
$(
go.Panel,
"Auto",
{
name: "MAIN_SHAPE",
width: 120,
height: 60,
},
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
$(
go.Shape,
"RoundedRectangle",
{
fill: "#B2F5EA",
stroke: "#333333",
strokeWidth: 1,
portId: "",
fromLinkable: true,
toLinkable: true,
cursor: "pointer",
},
new go.Binding("fill", "color"),
),
$(
go.TextBlock,
{
margin: 8,
font: "14px sans-serif",
editable: true,
},
new go.Binding("text").makeTwoWay(),
),
),
// Validation icon panel positioned at the top right
$(
go.Panel,
"Auto",
{
alignment: new go.Spot(1, 0, -5, 5), // Position at top right with 5px offset
width: 22,
height: 22,
isActionable: true,
cursor: "pointer",
click: (e, panel) => {
// Simple alert for demonstration
alert("Validation issues detected in this step!")
},
toolTip: createValidationTooltip(),
},
$(go.Shape, "Triangle", {
name: "VALIDATION_ICON",
width: 22,
height: 22,
fill: "white",
strokeWidth: 0,
isPanelMain: true,
spot1: go.Spot.TopLeft,
spot2: go.Spot.BottomRight,
}),
$(go.Shape, "Triangle", {
width: 20,
height: 18,
fill: "#FF5252",
strokeWidth: 2,
stroke: "#FF5252",
}),
$(go.Shape, {
geometryString: "M10,6 L10,12 M10,14 L10,16", // Simple exclamation mark
stroke: "white",
strokeWidth: 2,
}),
),
),
)
// Decision node template
diagram.nodeTemplateMap.add(
"Decision",
$(
go.Node,
"Auto",
{
width: 120,
height: 120,
selectionAdorned: true,
movable: true, // Explicitly set movable to true
opacity: 1, // Default opacity
},
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
$(
go.Shape,
"Diamond",
{
fill: "#FED7D7",
stroke: "#333333",
strokeWidth: 1,
portId: "",
fromLinkable: true,
toLinkable: true,
cursor: "pointer",
},
new go.Binding("fill", "color"),
),
$(
go.TextBlock,
{
margin: 8,
font: "14px sans-serif",
editable: true,
},
new go.Binding("text").makeTwoWay(),
),
),
)
// End node template
diagram.nodeTemplateMap.add(
"End",
$(
go.Node,
"Auto",
{
width: 120,
height: 60,
selectionAdorned: true,
movable: true, // Explicitly set movable to true
opacity: 1, // Default opacity
},
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
$(
go.Shape,
"RoundedRectangle",
{
fill: "#FEB2B2",
stroke: "#333333",
strokeWidth: 1,
portId: "",
fromLinkable: true,
toLinkable: true,
cursor: "pointer",
},
new go.Binding("fill", "color"),
),
$(
go.TextBlock,
{
margin: 8,
font: "14px sans-serif",
editable: true,
},
new go.Binding("text").makeTwoWay(),
),
),
)
// Define link template
diagram.linkTemplate = $(
go.Link,
{
routing: go.Link.AvoidsNodes,
curve: go.Link.JumpOver,
corner: 5,
toShortLength: 4,
relinkableFrom: true,
relinkableTo: true,
reshapable: true,
resegmentable: true,
// mouse-overs subtly highlight links:
mouseEnter: (e, link) => (link.findObject("HIGHLIGHT").stroke = "rgba(30,144,255,0.2)"),
mouseLeave: (e, link) => (link.findObject("HIGHLIGHT").stroke = "transparent"),
selectionAdorned: true,
},
new go.Binding("points").makeTwoWay(),
$(
go.Shape, // highlight shape, normally transparent
{ isPanelMain: true, strokeWidth: 8, stroke: "transparent", name: "HIGHLIGHT" },
),
$(
go.Shape, // the link path shape
{ isPanelMain: true, stroke: "#333333", strokeWidth: 2 },
),
$(
go.Shape, // the arrowhead
{ toArrow: "standard", stroke: null, fill: "#333333" },
),
$(
go.Panel,
"Auto", // the link label, normally not visible
{ visible: false, name: "LABEL", segmentIndex: 2, segmentFraction: 0.5 },
new go.Binding("visible", "text", (t) => !!t),
$(
go.Shape,
"RoundedRectangle", // the label shape
{ fill: "#F8F8F8", stroke: null },
),
$(
go.TextBlock, // the label
{
textAlign: "center",
font: "10pt sans-serif",
stroke: "#333333",
margin: 4,
},
new go.Binding("text", "text"),
),
),
)
// Add selection changed listener to handle opacity changes
diagram.addDiagramListener("ChangedSelection", (e) => {
const selectedNode = diagram.selection.first()
// Iterate through all nodes
diagram.nodes.each((node) => {
// If there's a selected node
if (selectedNode) {
// Set opacity based on whether the node is selected
node.opacity = node === selectedNode ? 1 : 0.5
} else {
// If no nodes are selected, set all nodes to opacity 1
node.opacity = 1
}
})
})
// When the diagram model changes, update app state
diagram.addDiagramListener("Modified", (e) => {
const model = diagram.model.toJson()
// You could save the model to state or localStorage here
})