in src/app/components/queue-v2/queues-v2.component.ts [97:442]
function queueVisualization(rawData : QueueInfo , componentInstance: QueueV2Component){
let numberOfNode = 0;
let isShowingDetails = false;
let selectedNode: any = null;
// Set this variable to 'horizontal' or 'vertical' to change orientation
// Define a type for the orientation
type Orientation = 'horizontal' | 'vertical';
// Declare orientation as the defined type
let orientation: Orientation = 'horizontal';
const duration = 500;
const svg = select('.visualize-area').append('svg')
.attr('width', '100%')
.attr('height', '100%');
function fitGraphScale() {
const baseSvgElem = svg.node() as SVGGElement;
const bounds = baseSvgElem.getBBox();
const parent = baseSvgElem.parentElement as HTMLElement;
const fullWidth = parent.clientWidth;
const fullHeight = parent.clientHeight;
const xfactor: number = fullWidth / bounds.width;
const yfactor: number = fullHeight / bounds.height;
let scaleFactor: number = Math.min(xfactor, yfactor);
const paddingPercent = 0.9;
scaleFactor = scaleFactor * paddingPercent;
return scaleFactor;
}
function centerGraph() {
const bbox = (svgGroup.node() as SVGGElement).getBBox();
const cx = bbox.x + bbox.width / 2;
const cy = bbox.y + bbox.height / 2;
return {cx, cy};
}
function adjustVisulizeArea(duration: number = 0) {
const scaleFactor = fitGraphScale();
const {cx, cy} = centerGraph();
svg.transition().duration(duration/1.5).call(zoom.translateTo, cx, cy)
.on("end", function() {
svg.transition().duration(duration/1.5).call(zoom.scaleBy, scaleFactor)
});
}
function changeOrientation() {
orientation = orientation === 'horizontal' ? 'vertical' : 'horizontal';
const root = d3hierarchy.hierarchy(rawData);
update(root);
// Update the position of existing plus circles, plus text, and queue names
svgGroup.selectAll('g.card')
.each(function(d: any) {
const group = select(this);
group.select('circle')
.attr("cx", orientation === 'horizontal' ? 300 : 150)
.attr("cy", orientation === 'horizontal' ? 60 : 120);
group.select('.plus-text')
.attr("x", orientation === 'horizontal' ? 300 : 150)
.attr("y", orientation === 'horizontal' ? 67 : 127);
});
}
const svgGroup = svg.append("g");
const fitButton = select(".fit-to-screen-button")
.on("click", function() {
adjustVisulizeArea(duration);
})
.on('mouseenter', function() {
select(this).select('.tooltip')
.style('visibility', 'visible')
.style('opacity', 1);
})
.on('mouseleave', function() {
select(this).select('.tooltip')
.style('visibility', 'hidden')
.style('opacity', 0);
});
const ortButton = select(".ort-button")
.on("click", function() {
changeOrientation();
setTimeout(
() => {
const fitButton = document.getElementById('fitButton');
if (fitButton) {
fitButton.click();
}
}, duration);
});
const treelayout = d3flextree
.flextree<QueueInfo>({})
.nodeSize((d) => {
return orientation === 'horizontal' ? [300, 600] : [300, 300];
})
.spacing(() => orientation === 'horizontal' ? 100 : 300);
const zoom = d3zoom
.zoom<SVGSVGElement, unknown>()
.scaleExtent([0.1, 5])
.on("zoom", (event) => {
svgGroup.attr("transform", event.transform);
});
svg.call(zoom);
const root = d3hierarchy.hierarchy(rawData);
update(root);
function update(source: any) {
var treeData = treelayout(root);
var nodes = treeData.descendants();
var node = svgGroup
.selectAll<SVGGElement, d3hierarchy.HierarchyNode<QueueInfo>>('g.card')
.data(nodes, function(d: any) {
return d.id || (d.id = ++numberOfNode);
});
var nodeEnter = node
.enter().append('g')
.attr('class', 'card')
.attr("transform", function() {
if (source.y0 !== undefined && source.x0 !== undefined) {
return orientation === 'horizontal'
? `translate(${source.y0},${source.x0})`
: `translate(${source.x0},${source.y0})`;
} else {
return orientation === 'horizontal'
? `translate(${source.y},${source.x})`
: `translate(${source.x},${source.y})`;
}
});
nodeEnter.each(function(d) {
const group = select(this);
const queueName = d.data.queueName?.split(".").at(-1) ?? d.data.queueName;
group.append("rect")
.attr("width", 300)
.attr("height", 120)
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-width", 2)
.attr("rx", 10)
.attr("ry", 10)
.attr("class", "cardMain");
group.append("rect")
.attr("width", 300)
.attr("height", 30)
.attr("fill", "#d4eaf7")
.attr("class", "cardTop");
group.append("rect")
.attr("y", 30)
.attr("width", 300)
.attr("height", 60)
.attr("fill", "white")
.attr("class", "cardMiddle");
group.append("rect")
.attr("y", 90)
.attr("width", 300)
.attr("height", 30)
.attr("fill", "#e6f4ea")
.attr("class", "cardBottom");
group.append("image")
.attr("href", "./assets/images/hierarchy.svg")
.attr("x", 5)
.attr("y", 5)
.attr("width", 20)
.attr("height", 20);
group.append("text")
.attr("x", 30)
.attr("y", 22.5)
.attr("font-size", "25px")
.attr("fill", "black")
.text(queueName)
.call(ellipsis, 270)
.call(tooltip, group, queueName);
const plusCircle = group.append("circle")
.attr("cx", () => orientation === 'horizontal' ? 300 : 150) // Right side if horizontal, center if vertical
.attr("cy", () => orientation === 'horizontal' ? 60 : 120) // Center if horizontal, bottom if vertical
.attr("r", 20)
.attr("fill", "white")
.attr("stroke", "black")
.attr("stroke-width", 1)
.style("visibility", "hidden")
.on('click', function(event) {
event.stopPropagation();
click(event, d);
});
const plusText = group.append("text")
.classed("plus-text", true)
.attr("x", () => orientation === 'horizontal' ? 300 : 150)
.attr("y", () => orientation === 'horizontal' ? 67 : 127)
.attr("text-anchor", "middle")
.attr("font-size", "20px")
.attr("fill", "black")
.text("+")
.attr("pointer-events", "none")
.style("visibility", "hidden");
if (d.children) {
group.on("mouseover", function() {
plusCircle.style("visibility", "visible");
plusText.style("visibility", "visible");
});
}
group.on("click", function() {
if(selectedNode == this || selectedNode == null){
isShowingDetails = !isShowingDetails;
} else {
select(selectedNode).select('.cardMain').attr("stroke", "white")
.attr("stroke-width", 2);
select(selectedNode).select('.cardTop').attr("fill", "#d4eaf7");
}
selectedNode = this;
componentInstance.seletedInfo = d.data;
if(isShowingDetails){
console.log("showing details", componentInstance.seletedInfo);
select(this).select('.cardMain').attr("stroke-width", 8)
.attr("stroke", "#50505c");
select(this).select('.cardTop').attr("fill", "#95d5f9");
select(".additional-info-element").style("display", "block");
} else {
select(this).select('.cardMain').attr("stroke-width", 2)
.attr("stroke", "white");
select(this).select('.cardTop').attr("fill", "#d4eaf7");
select(".additional-info-element").style("display", "none");
}
adjustVisulizeArea(duration);
});
group.on("mouseout", function() {
plusCircle.style("visibility", "hidden");
plusText.style("visibility", "hidden");
});
plusCircle.on("mouseover", function() {
select(this).attr("fill", "grey");
});
plusCircle.on("mouseout", function() {
select(this).attr("fill", "white");
});
});
const nodeUpdate = nodeEnter.merge(node)
.attr("stroke", "black");
nodeUpdate.transition()
.duration(duration)
.attr("transform", function(this: SVGGElement, event: any, i: any, arr: any) {
const d: any = select(this).datum();
return orientation === 'horizontal'
? `translate(${d.y},${d.x})`
: `translate(${d.x},${d.y})`;
});
nodeUpdate.select('.cardBottom')
.style("fill", function(d: any) {
return d._children ? "#9fc6aa" : "#e6f4ea";
});
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(this: SVGGElement, event: any, i: any, arr: any) {
const d = select(this).datum();
return orientation === 'horizontal'
? `translate(${source.y},${source.x})`
: `translate(${source.x},${source.y})`;
})
.remove();
const links = treeData.links();
let link = svgGroup.selectAll<SVGPathElement, d3hierarchy.HierarchyPointLink<QueueInfo>>('path.link')
.data(links, function(d: any) { return d.target.id; });
const linkEnter = link.enter().insert('path', "g")
.attr("class", "link")
.attr('d', d => {
const o = orientation === 'horizontal'
? {y: source.y0 || source.y, x: source.x0 || source.x}
: {x: source.x0 || source.x, y: source.y0 || source.y};
return diagonal(o, o, orientation);
})
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", "2px");
const linkUpdate = linkEnter.merge(link);
linkUpdate.transition()
.duration(duration)
.attr('d', d => diagonal(d.source, d.target, orientation));
const linkExit = link.exit().transition()
.duration(duration)
.attr('d', d => {
const o = orientation === 'horizontal'
? {y: source.y, x: source.x}
: {x: source.x, y: source.y};
return diagonal(o, o, orientation);
})
.remove();
nodes.forEach(function(d: any) {
d.x0 = d.x;
d.y0 = d.y;
});
function click(event: MouseEvent, d: any) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
}
}