Skip to content

Commit 41bb116

Browse files
committed
Activity: Added hover effect in dependency graph
1 parent ae61e86 commit 41bb116

File tree

2 files changed

+71
-28
lines changed

2 files changed

+71
-28
lines changed

src/app/component/dependency-graph/dependency-graph.component.css

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,26 @@ circle {
77
fill: none;
88
stroke: #000;
99
stroke-width: 1.5px;
10-
}
10+
}
11+
12+
.clickable {
13+
cursor: pointer;
14+
}
15+
16+
:host ::ng-deep svg.dependency-graph g .node-rect {
17+
fill: var(--node-fill);
18+
stroke: var(--node-border);
19+
stroke-width: 1.5;
20+
}
21+
22+
:host ::ng-deep svg.dependency-graph g.clickable {
23+
cursor: pointer;
24+
}
25+
26+
:host ::ng-deep svg.dependency-graph g.hovered .node-rect {
27+
fill: var(--node-hover-fill);
28+
}
29+
30+
:host ::ng-deep svg.dependency-graph g.main text {
31+
fill: var(--text-primary, inherit);
32+
}

src/app/component/dependency-graph/dependency-graph.component.ts

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,10 @@ export class DependencyGraphComponent implements OnInit, OnChanges {
112112
populateGraphWithActivitiesCurrentActivityDependsOn(activity: Activity): void {
113113
if (activity.dependsOn) {
114114
let i: number = 1;
115-
for (const prececcor of activity.dependsOn) {
116-
this.addNode(prececcor, -1, i++);
115+
for (const predecessor of activity.dependsOn) {
116+
this.addNode(predecessor, -1, i++);
117117
this.graphData['links'].push({
118-
source: prececcor,
118+
source: predecessor,
119119
target: activity.name,
120120
});
121121
}
@@ -224,17 +224,20 @@ export class DependencyGraphComponent implements OnInit, OnChanges {
224224
this.activityClicked.emit(d.id);
225225
}
226226
})
227-
.on('mouseover', (event: MouseEvent, d: any) => {
228-
if (this.activityClicked.observed) {
229-
if (d.relativeLevel != 0) {
230-
d3.select(event.currentTarget as Element).style('cursor', 'pointer');
231-
} else {
232-
d3.select(event.currentTarget as Element).style('cursor', 'default');
233-
}
234-
}
227+
.classed('clickable', (d: any) => this.activityClicked.observed && d.relativeLevel != 0)
228+
.classed('predecessor', (d: any) => d.relativeLevel < 0)
229+
.classed('successor', (d: any) => d.relativeLevel > 0)
230+
.classed('main', (d: any) => d.relativeLevel == 0)
231+
.style('cursor', (d: any) =>
232+
this.activityClicked.observed && d.relativeLevel != 0 ? 'pointer' : 'default'
233+
)
234+
.on('mouseover', function (_event: any, _d: any) {
235+
if (d3.select(this).classed('clickable')) {
236+
d3.select(this).classed('hovered', true);
237+
}
235238
})
236-
.on('mouseout', (event: MouseEvent, d: any) => {
237-
d3.select(event.currentTarget as Element).style('cursor', 'default');
239+
.on('mouseout', function (_event: any, _d: any) {
240+
d3.select(this).classed('hovered', false);
238241
});
239242

240243
const rectHeight = 30;
@@ -255,29 +258,47 @@ export class DependencyGraphComponent implements OnInit, OnChanges {
255258
const self = this;
256259
nodes.each(function (this: SVGGElement, d: any) {
257260
const textElem = d3.select(this).select('text').node() as SVGTextElement;
258-
let textWidth = 60; // fallback default
259-
if (textElem && textElem.getBBox) {
260-
textWidth = textElem.getBBox().width;
261+
let textWidth = 60;
262+
if (textElem) {
263+
try {
264+
textWidth = (textElem.getBBox && textElem.getBBox().width) || textWidth;
265+
} catch {
266+
textWidth =
267+
(textElem.getComputedTextLength && textElem.getComputedTextLength()) || textWidth;
268+
}
261269
}
262-
const rectWidth = textWidth + padding;
263-
d.rectWidth = rectWidth; // Store for collision force
264-
// Insert rect before text
270+
271+
const rectWidth = Math.ceil(textWidth + padding);
272+
d.rectWidth = rectWidth; // store for collision force
273+
274+
// compute fill + hover color once
275+
const fillColor =
276+
d.relativeLevel == 0
277+
? self.themeColors.mainNodeFill || 'green'
278+
: (d.relativeLevel < 0
279+
? self.themeColors.predecessorFill
280+
: self.themeColors.successorFill) || 'white';
281+
const c = d3.color(fillColor);
282+
const hoverColor = c ? c.darker(0.6).toString() : fillColor;
283+
284+
// set CSS variables on the group so global stylesheet can use them
285+
d3.select(this)
286+
.style('--node-fill', fillColor)
287+
.style('--node-hover-fill', hoverColor)
288+
.style('--node-border', self.themeColors.borderColor || 'black');
289+
290+
// Insert rect before text sized to measured text
265291
d3.select(this)
266292
.insert('rect', 'text')
293+
.attr('class', 'node-rect')
267294
.attr('x', -rectWidth / 2)
268295
.attr('y', -rectHeight / 2)
269296
.attr('width', rectWidth)
270297
.attr('height', rectHeight)
271298
.attr('rx', rectRx)
272299
.attr('ry', rectRy)
273-
.attr('fill', (d: any) => {
274-
if (d.relativeLevel == 0) return self.themeColors.mainNodeFill || 'green';
275-
let col: string | undefined =
276-
d.relativeLevel < 0 ? self.themeColors.predecessorFill : self.themeColors.successorFill;
277-
return col || 'white';
278-
})
279-
.attr('stroke', self.themeColors.borderColor || 'black')
280-
.attr('stroke-width', 1.5);
300+
.attr('stroke-width', 1.5)
301+
.attr('stroke', 'currentColor'); // stroke taken from --node-border via CSS
281302
});
282303

283304
this.simulation.nodes(this.graphData['nodes']).on('tick', () => {

0 commit comments

Comments
 (0)