Skip to content

Commit e8d7bb2

Browse files
committed
Dependency: Support dark mode
1 parent 5e617ba commit e8d7bb2

File tree

4 files changed

+123
-76
lines changed

4 files changed

+123
-76
lines changed

src/app/component/activity-description/activity-description.component.html

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@ <h1>
5555
<p id="assessment" [innerHTML]="currentActivity.assessment?.render()"></p>
5656
</mat-expansion-panel>
5757

58+
<mat-expansion-panel>
59+
<mat-expansion-panel-header>
60+
<mat-panel-title>
61+
<b>Dependencies</b>
62+
</mat-panel-title>
63+
</mat-expansion-panel-header>
64+
<div *ngIf="currentActivity?.name">
65+
<app-dependency-graph [activityName]="currentActivity?.name || ''"></app-dependency-graph>
66+
</div>
67+
</mat-expansion-panel>
68+
5869
<mat-expansion-panel *ngIf="currentActivity.implementationGuide?.hasContent()">
5970
<mat-expansion-panel-header>
6071
<mat-panel-title>
@@ -231,17 +242,6 @@ <h1>
231242
</ng-template>
232243
</mat-expansion-panel>
233244

234-
<mat-expansion-panel>
235-
<mat-expansion-panel-header>
236-
<mat-panel-title>
237-
<b>Dependencies</b>
238-
</mat-panel-title>
239-
</mat-expansion-panel-header>
240-
<div *ngIf="currentActivity?.name">
241-
<app-dependency-graph [activityName]="currentActivity?.name || ''"></app-dependency-graph>
242-
</div>
243-
</mat-expansion-panel>
244-
245245
<mat-expansion-panel>
246246
<mat-expansion-panel-header>
247247
<mat-panel-title>

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

Lines changed: 88 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as d3 from 'd3';
33
import { LoaderService } from '../../service/loader/data-loader.service';
44
import { Activity } from 'src/app/model/activity-store';
55
import { DataStore } from 'src/app/model/data-store';
6+
import { ThemeService } from 'src/app/service/theme.service';
67

78
export interface graphNodes {
89
id: string;
@@ -21,25 +22,35 @@ export interface graph {
2122
links: graphLinks[];
2223
}
2324

25+
interface ThemeColors {
26+
linkColor: string;
27+
borderColor: string;
28+
mainNodeColor: string;
29+
mainNodeFill: string;
30+
predecessorFill: string;
31+
successorFill: string;
32+
}
33+
2434
@Component({
2535
selector: 'app-dependency-graph',
2636
templateUrl: './dependency-graph.component.html',
2737
styleUrls: ['./dependency-graph.component.css'],
2838
})
2939
export class DependencyGraphComponent implements OnInit, OnChanges {
30-
COLOR_OF_LINK: string = 'black';
31-
COLOR_OF_NODE: string = '#66bb6a';
32-
COLOR_OF_PREDECESSOR: string = '#deeedeff';
33-
COLOR_OF_SUCCESSOR: string = '#fdfdfdff';
34-
BORDER_COLOR_OF_NODE: string = 'black';
40+
css: CSSStyleDeclaration = getComputedStyle(document.body);
41+
themeColors: Partial<ThemeColors> = {};
42+
theme: string;
3543
simulation: any;
3644
dataStore: Partial<DataStore> = {};
3745
graphData: graph = { nodes: [], links: [] };
3846
visited: Set<string> = new Set();
3947

4048
@Input() activityName: string = '';
4149

42-
constructor(private loader: LoaderService) {}
50+
constructor(private loader: LoaderService, private themeService: ThemeService) {
51+
this.theme = this.themeService.getTheme();
52+
this.setThemeColors(this.theme);
53+
}
4354

4455
ngOnInit(): void {
4556
this.loader.load().then((dataStore: DataStore) => {
@@ -50,6 +61,11 @@ export class DependencyGraphComponent implements OnInit, OnChanges {
5061
}
5162
this.populateGraph(this.activityName);
5263
});
64+
65+
// Reactively handle theme changes (if user toggles later)
66+
this.themeService.theme$.subscribe((theme: string) => {
67+
this.setThemeColors(theme);
68+
});
5369
}
5470

5571
ngOnChanges(changes: SimpleChanges): void {
@@ -61,6 +77,19 @@ export class DependencyGraphComponent implements OnInit, OnChanges {
6177
}
6278
}
6379

80+
setThemeColors(theme: string) {
81+
/* eslint-disable */
82+
this.themeColors.mainNodeFill = this.css.getPropertyValue('--heatmap-filled').trim();
83+
this.themeColors.mainNodeColor = this.css.getPropertyValue('--text-primary').trim();
84+
this.themeColors.linkColor = this.css.getPropertyValue('--dependency-link').trim();
85+
this.themeColors.borderColor = this.css.getPropertyValue('--dependency-border').trim();
86+
this.themeColors.predecessorFill = this.css.getPropertyValue('--dependency-predecessor-fill').trim();
87+
this.themeColors.successorFill = this.css.getPropertyValue('--dependency-successor-fill').trim();
88+
/*eslint-enable */
89+
90+
this.generateGraph();
91+
}
92+
6493
populateGraph(activityName: string): void {
6594
if (this.simulation) {
6695
this.simulation.stop();
@@ -74,7 +103,7 @@ export class DependencyGraphComponent implements OnInit, OnChanges {
74103
this.populateGraphWithActivitiesCurrentActivityDependsOn(activity);
75104
this.populateGraphWithActivitiesThatDependsOnCurrentActivity(activity);
76105

77-
this.generateGraph(this.activityName);
106+
this.generateGraph();
78107
}
79108
}
80109

@@ -138,7 +167,7 @@ export class DependencyGraphComponent implements OnInit, OnChanges {
138167
return d.relativeLevel * 30;
139168
}
140169

141-
generateGraph(activityName: string): void {
170+
generateGraph(): void {
142171
let svg = d3.select('svg');
143172
svg.selectAll('*').remove();
144173

@@ -174,7 +203,7 @@ export class DependencyGraphComponent implements OnInit, OnChanges {
174203
.attr('overflow', 'visible')
175204
.append('svg:path')
176205
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
177-
.attr('fill', this.COLOR_OF_LINK)
206+
.attr('fill', this.themeColors.linkColor || 'black')
178207
.style('stroke', 'none');
179208

180209
let links = svg
@@ -184,7 +213,7 @@ export class DependencyGraphComponent implements OnInit, OnChanges {
184213
.data(this.graphData['links'])
185214
.enter()
186215
.append('line')
187-
.style('stroke', this.COLOR_OF_LINK)
216+
.style('stroke', this.themeColors.linkColor || 'black')
188217
.attr('marker-end', 'url(#arrowhead)');
189218

190219
let nodes = svg
@@ -229,10 +258,12 @@ export class DependencyGraphComponent implements OnInit, OnChanges {
229258
.attr('rx', rectRx)
230259
.attr('ry', rectRy)
231260
.attr('fill', (d: any) => {
232-
if (d.relativeLevel == 0) return self.COLOR_OF_NODE;
233-
return d.relativeLevel < 0 ? self.COLOR_OF_PREDECESSOR : self.COLOR_OF_SUCCESSOR;
261+
if (d.relativeLevel == 0) return self.themeColors.mainNodeFill || 'green';
262+
let col: string | undefined =
263+
d.relativeLevel < 0 ? self.themeColors.predecessorFill : self.themeColors.successorFill;
264+
return col || 'white';
234265
})
235-
.attr('stroke', self.BORDER_COLOR_OF_NODE)
266+
.attr('stroke', self.themeColors.borderColor || 'black')
236267
.attr('stroke-width', 1.5);
237268
});
238269

@@ -244,51 +275,49 @@ export class DependencyGraphComponent implements OnInit, OnChanges {
244275
this.simulation.force('link').links(this.graphData['links']);
245276

246277
function ticked() {
247-
if (self.simulation.alpha() < 1.9) {
248-
// Improved rectangle edge intersection for arrowhead placement
249-
links
250-
.attr('x1', function (d: any) {
251-
return d.source.x;
252-
})
253-
.attr('y1', function (d: any) {
254-
return d.source.y;
255-
})
256-
.attr('x2', function (d: any) {
257-
// If target has rectWidth, adjust arrow to edge minus offset
258-
if (d.target.rectWidth) {
259-
const pt = self.rectEdgeIntersection(
260-
d.source.x,
261-
d.source.y,
262-
d.target.x,
263-
d.target.y,
264-
d.target.rectWidth,
265-
30,
266-
10 // rectHeight, offset
267-
);
268-
return pt.x;
269-
}
270-
return d.target.x;
271-
})
272-
.attr('y2', function (d: any) {
273-
if (d.target.rectWidth) {
274-
const pt = self.rectEdgeIntersection(
275-
d.source.x,
276-
d.source.y,
277-
d.target.x,
278-
d.target.y,
279-
d.target.rectWidth,
280-
30,
281-
10
282-
);
283-
return pt.y;
284-
}
285-
return d.target.y;
286-
});
287-
288-
nodes.attr('transform', function (d: any) {
289-
return 'translate(' + d.x + ',' + d.y + ')';
278+
// Improved rectangle edge intersection for arrowhead placement
279+
links
280+
.attr('x1', function (d: any) {
281+
return d.source.x;
282+
})
283+
.attr('y1', function (d: any) {
284+
return d.source.y;
285+
})
286+
.attr('x2', function (d: any) {
287+
// If target has rectWidth, adjust arrow to edge minus offset
288+
if (d.target.rectWidth) {
289+
const pt = self.rectEdgeIntersection(
290+
d.source.x,
291+
d.source.y,
292+
d.target.x,
293+
d.target.y,
294+
d.target.rectWidth,
295+
30,
296+
10 // rectHeight, offset
297+
);
298+
return pt.x;
299+
}
300+
return d.target.x;
301+
})
302+
.attr('y2', function (d: any) {
303+
if (d.target.rectWidth) {
304+
const pt = self.rectEdgeIntersection(
305+
d.source.x,
306+
d.source.y,
307+
d.target.x,
308+
d.target.y,
309+
d.target.rectWidth,
310+
30,
311+
10
312+
);
313+
return pt.y;
314+
}
315+
return d.target.y;
290316
});
291-
}
317+
318+
nodes.attr('transform', function (d: any) {
319+
return 'translate(' + d.x + ',' + d.y + ')';
320+
});
292321
}
293322
}
294323

@@ -364,8 +393,8 @@ export class DependencyGraphComponent implements OnInit, OnChanges {
364393
for (i = 0; i < n; ++i) {
365394
node = nodes[i];
366395
// Calculate bounding box for node
367-
nx1 = node.x - node.rectWidth / 2 - 10;
368-
nx2 = node.x + node.rectWidth / 2 + 10;
396+
nx1 = node.x - node.rectWidth / 2 - 25;
397+
nx2 = node.x + node.rectWidth / 2 + 25;
369398
ny1 = node.y - 25;
370399
ny2 = node.y + 25;
371400
for (let j = i + 1; j < n; ++j) {

src/assets/Markdown Files/TODO-v4.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
## Doing
2+
### Dependency graph
3+
4+
- Heatmap: Handle dependsOn uuid (example)
5+
- http://localhost:4200/activity-description?uuid=13e9757e-58e2-4277-bc0f-eadc674891e6
6+
7+
- Dependency graph: Make connecting nodes clickable
8+
- Heatmap: Add #uuid to URL
9+
10+
## Next
211
- Teams: Bug: Reads progress heading from activityStore, not metaStore
312
- Team KPI: One KPI per ProgressDefinition
413
- KPI: Add Sub-title
514

6-
## Next
7-
### Dependency graph
8-
- Dependency graph: Add to CircularHeatmap Details
9-
- Matrix: Dependency graph: Render in center of page
10-
1115
## ToDo
1216
- Heatmap: Fix: asterisk marks when modified
1317
- ViewController needs to know about changes vs temp storage
@@ -70,6 +74,9 @@
7074
- Meta.yaml: Allow admins to customize the terms 'Team' and 'Group' (e.g. to 'App' and 'Portfolio')
7175

7276
# Done
77+
- Matrix: Dependency graph: Render in center of page
78+
- Dependency graph: Add to CircularHeatmap Details
79+
- Dependency graph: Support dark mode
7380
- Merge in Dark Mode [PR #381](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/pull/381)
7481
- Linting
7582
- Using Angular's built-in DomSanitizer to check [innerHTML]

src/custom-theme.scss

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@ body {
100100
--heatmap-cursor-hover: #1c8b1c;
101101
--heatmap-cursor-selected:#3d3d3d;
102102

103+
--dependency-link: #707070;
104+
--dependency-border: #222222;
105+
--dependency-mainnode-fill: #4caf50;
106+
--dependency-predecessor-fill: #deeedeff;
107+
--dependency-successor-fill: #fdfdfdff;
108+
103109
@include mat.all-component-themes($DSOMM-light-theme);
104110
}
105111

@@ -123,7 +129,12 @@ body.dark-theme {
123129
--heatmap-stroke: #000000;
124130
--heatmap-cursor-hover: #145e14;
125131
--heatmap-cursor-selected: #232323;
126-
132+
133+
--dependency-link: #bbbbbb;
134+
--dependency-border: #0e1b0e;
135+
--dependency-mainnode-fill: rgb(107, 190, 107);
136+
--dependency-predecessor-fill: rgb(172, 206, 172);
137+
--dependency-successor-fill: rgb(192, 192, 192);
127138

128139
.title-button,
129140
h1, h2, h3, h4, h5, h6 {

0 commit comments

Comments
 (0)