Skip to content

Dark Theme v2.0 #381

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component, OnInit } from '@angular/core';
import { ThemeService } from './service/theme.service';

@Component({
selector: 'app-root',
Expand All @@ -9,6 +10,10 @@ export class AppComponent implements OnInit {
title = 'DSOMM';
menuIsOpen: boolean = true;

constructor(private themeService: ThemeService) {
this.themeService.initTheme();
}

ngOnInit(): void {
let menuState: string | null = localStorage.getItem('state.menuIsOpen');
if (menuState === 'false') {
Expand Down
20 changes: 15 additions & 5 deletions src/app/component/circular-heatmap/circular-heatmap.component.css
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,26 @@
}

.overlay-close {
border: black solid 1px;
background-color: rgba(0, 0, 0, 0);
border: none;
color: black;
background-color: rgba(0, 0, 0, 0);
grid-column: 2/3;
grid-row: 1/4;
display: grid;
justify-content: top;
justify-content: center;
align-items: start;
margin-left: auto;
}

/* overlay-close - light theme */
:host-context(body.light-theme) .overlay-close {
color: black;
}

/* overlay-close - dark theme */
:host-context(body.dark-theme) .overlay-close {
color: white;
}

.team-filter {
padding: 0.4rem;
grid-column: 2/3;
Expand Down Expand Up @@ -161,6 +171,6 @@ button.filter-toggle {

.overlay-details {
width: 100%;
}
}

}
73 changes: 50 additions & 23 deletions src/app/component/circular-heatmap/circular-heatmap.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
ModalMessageComponent,
DialogInfo,
} from '../modal-message/modal-message.component';
import { ThemeService } from '../../service/theme.service';

export interface activitySchema {
uuid: string;
Expand Down Expand Up @@ -63,27 +64,56 @@ export class CircularHeatmapComponent implements OnInit {
showOverlay: boolean;
showFilters: boolean;
markdown: md = md();
theme: string;
theme_colors!: Record<string, string>;

constructor(
private yaml: ymlService,
private router: Router,
private themeService: ThemeService,
public modal: ModalMessageComponent
) {
this.showOverlay = false;
this.showFilters = true;
this.theme = this.themeService.getTheme();
}

ngOnInit(): void {
console.log(`${this.perfNow()}s: ngOnInit`);
// Ensure that Levels and Teams load before MaturityData
// using promises, since ngOnInit does not support async/await
this.LoadMaturityLevels()
.then(() => this.LoadTeamsFromMetaYaml())
.then(() => this.LoadMaturityDataFromGeneratedYaml())
.then(() => {
console.log(`${this.perfNow()}s: set filters: ${this.chips?.length}`);
this.matChipsArray = this.chips.toArray();
});
const savedTheme = this.themeService.getTheme() || 'light';
this.themeService.setTheme(savedTheme); // sets .light-theme or .dark-theme

requestAnimationFrame(() => {
// Now the DOM has the correct class and CSS vars are live
const css = getComputedStyle(document.body);
this.theme_colors = {
background: css.getPropertyValue('--heatmap-background').trim(),
filled: css.getPropertyValue('--heatmap-filled').trim(),
disabled: css.getPropertyValue('--heatmap-disabled').trim(),
cursor: css.getPropertyValue('--heatmap-cursor').trim(),
stroke: css.getPropertyValue('--heatmap-stroke').trim(),
};

this.LoadMaturityLevels()
.then(() => this.LoadTeamsFromMetaYaml())
.then(() => this.LoadMaturityDataFromGeneratedYaml())
.then(() => {
this.matChipsArray = this.chips.toArray();
});
});

// Reactively handle theme changes (if user toggles later)
this.themeService.theme$.subscribe((theme: string) => {
const css = getComputedStyle(document.body);
this.theme_colors = {
background: css.getPropertyValue('--heatmap-background').trim(),
filled: css.getPropertyValue('--heatmap-filled').trim(),
disabled: css.getPropertyValue('--heatmap-disabled').trim(),
cursor: css.getPropertyValue('--heatmap-cursor').trim(),
stroke: css.getPropertyValue('--heatmap-stroke').trim(),
};

this.reColorHeatmap(); // repaint segments with new theme
});
}

@ViewChildren(MatChip) chips!: QueryList<MatChip>;
Expand Down Expand Up @@ -405,7 +435,6 @@ export class CircularHeatmapComponent implements OnInit {
.innerRadius(innerRadius)
.segmentHeight(segmentHeight)
.domain([0, 1])
.range(['white', 'green'])
.radialLabels(radial_labels)
.segmentLabels(segment_labels);

Expand Down Expand Up @@ -498,6 +527,7 @@ export class CircularHeatmapComponent implements OnInit {
var segmentLabels: any[] = [];

//console.log(segmentLabels)
let _self: any = this;

function chart(selection: any) {
selection.each(function (this: any, data: any) {
Expand Down Expand Up @@ -548,7 +578,7 @@ export class CircularHeatmapComponent implements OnInit {
.startAngle(sa)
.endAngle(ea)
)
.attr('stroke', '#252525')
.attr('stroke', _self.theme_colors['stroke'])
.attr('fill', function (d) {
return color(accessor(d));
});
Expand Down Expand Up @@ -610,17 +640,11 @@ export class CircularHeatmapComponent implements OnInit {
cursors
.append('path')
.attr('id', 'hover')
.attr('pointer-events', 'none')
.attr('stroke', 'green')
.attr('stroke-width', '7')
.attr('fill', 'transparent');
.attr('pointer-events', 'none');
cursors
.append('path')
.attr('id', 'selected')
.attr('pointer-events', 'none')
.attr('stroke', '#232323')
.attr('stroke-width', '4')
.attr('fill', 'transparent');
.attr('pointer-events', 'none');
});
}

Expand Down Expand Up @@ -716,7 +740,7 @@ export class CircularHeatmapComponent implements OnInit {
noActivitytoGrey(): void {
for (var x = 0; x < this.ALL_CARD_DATA.length; x++) {
if (this.ALL_CARD_DATA[x]['Done%'] == -1) {
d3.select('#index-' + x).attr('fill', '#DCDCDC');
d3.select('#index-' + x).attr('fill', this.theme_colors['disabled']);
}
}
}
Expand Down Expand Up @@ -822,7 +846,7 @@ export class CircularHeatmapComponent implements OnInit {
var colorSector = d3
.scaleLinear<string, string>()
.domain([0, 1])
.range(['white', 'green']);
.range([this.theme_colors['background'], this.theme_colors['filled']]);

if (cntAll !== 0) {
this.ALL_CARD_DATA[index]['Done%'] = cntTrue / cntAll;
Expand All @@ -833,7 +857,10 @@ export class CircularHeatmapComponent implements OnInit {
} else {
this.ALL_CARD_DATA[index]['Done%'] = -1;
// console.log(`${this.ALL_CARD_DATA[index].SubDimension} ${this.ALL_CARD_DATA[index].Level} None`);
d3.select('#index-' + index).attr('fill', '#DCDCDC');
d3.select('#index-' + index).attr(
'fill',
this.theme_colors['disabled']
);
}
}
}
Expand Down
48 changes: 42 additions & 6 deletions src/app/component/dependency-graph/dependency-graph.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Component, OnInit, Input } from '@angular/core';
import { Component, OnInit, Input, ElementRef } from '@angular/core';
import * as d3 from 'd3';
import { ymlService } from 'src/app/service/yaml-parser/yaml-parser.service';
import { Subscription } from 'rxjs';
import { ThemeService } from '../../service/theme.service';

export interface graphNodes {
id: string;
Expand Down Expand Up @@ -35,10 +37,24 @@ export class DependencyGraphComponent implements OnInit {
@Input() subDimension: string = '';
@Input() activityName: string = '';

constructor(private yaml: ymlService) {}
private themeSub: Subscription | undefined;
currentTheme: string = 'light'; // default

constructor(
private yaml: ymlService,
private elementRef: ElementRef,
private themeService: ThemeService
) {}

ngOnInit(): void {
this.yaml.setURI('./assets/YAML/generated/generated.yaml');

this.currentTheme = this.themeService.getTheme();
this.themeSub = this.themeService.theme$.subscribe(theme => {
this.currentTheme = theme;
this.applyTextColor(theme);
});

// Function sets data
this.yaml.getJson().subscribe(data => {
this.graphData = { nodes: [], links: [] };
Expand Down Expand Up @@ -108,7 +124,26 @@ export class DependencyGraphComponent implements OnInit {
}
}

applyTextColor(theme: string): void {
const fill = theme === 'dark' ? '#ffffff' : '#000000';
const selectedNodeColor = theme === 'dark' ? '#666666' : 'yellow';
const defaultNodeColor = this.COLOR_OF_NODE;

d3.select(this.elementRef.nativeElement)
.selectAll('text')
.attr('fill', fill);

d3.select(this.elementRef.nativeElement)
.selectAll('circle')
.attr('fill', (d: any) =>
d.id === this.activityName ? selectedNodeColor : defaultNodeColor
);
}

generateGraph(activity: string): void {
const selectedNodeColor =
this.currentTheme === 'dark' ? '#666666' : 'yellow';

let svg = d3.select('svg'),
width = +svg.attr('width'),
height = +svg.attr('height');
Expand Down Expand Up @@ -162,10 +197,9 @@ export class DependencyGraphComponent implements OnInit {
node
.append('circle')
.attr('r', 10)
.attr('fill', function (d) {
if (d.id == activity) return 'yellow';
else return defaultNodeColor;
});
.attr('fill', (d: any) =>
d.id === this.activityName ? selectedNodeColor : defaultNodeColor
);

node
.append('text')
Expand All @@ -175,6 +209,8 @@ export class DependencyGraphComponent implements OnInit {
return d.id;
});

this.applyTextColor(this.currentTheme);

this.simulation.nodes(this.graphData['nodes']).on('tick', ticked);

this.simulation.force('link').links(this.graphData['links']);
Expand Down
13 changes: 12 additions & 1 deletion src/app/component/matrix/matrix.component.css
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,20 @@
.tags-activity {
font-weight: 800;
font-style: italic;
color: rgb(0, 113, 151);
font-size: 12px;
}
/*tag activity - light */
:host-context(body.light-theme) .tags-activity,
:host-context(body.light-theme) .tags-activity span {
color: rgb(0, 113, 151);
}

/*tag activity - dark */
:host-context(body.dark-theme) .tags-activity,
:host-context(body.dark-theme) .tags-activity span {
color: #397af4;
}

.reset-button {
background-color: #66bb6a;
display: block;
Expand Down
15 changes: 15 additions & 0 deletions src/app/component/sidenav-buttons/sidenav-buttons.component.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<mat-nav-list>
<!-- Dynamic nav links -->
<a
mat-list-item
*ngFor="let option of Options; index as i"
Expand All @@ -7,3 +8,17 @@
<h3 mat-line>{{ Options[i] }}</h3>
</a>
</mat-nav-list>

<!-- Separate theme toggle outside nav-list -->
<mat-divider></mat-divider>

<mat-list>
<mat-list-item (click)="toggleTheme()" style="cursor: pointer">
<mat-icon mat-list-icon color="primary">
{{ isNightMode ? 'light_mode' : 'dark_mode' }}
</mat-icon>
<h3 mat-line>
{{ isNightMode ? 'Switch to Light Mode' : 'Switch to Dark Mode' }}
</h3>
</mat-list-item>
</mat-list>
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('SidenavButtonsComponent', () => {

it('check for navigation names being shown in the same order as options array', () => {
const HTMLElement: HTMLElement = fixture.nativeElement;
const NavigationList = HTMLElement.querySelectorAll('h3')!;
const NavigationList = HTMLElement.querySelectorAll('a > h3')!;
let NavigationNamesBeingShown = [];
for (var x = 0; x < NavigationList.length; x += 1) {
NavigationNamesBeingShown.push(NavigationList[x].textContent);
Expand Down
21 changes: 18 additions & 3 deletions src/app/component/sidenav-buttons/sidenav-buttons.component.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Component } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { ThemeService } from '../../service/theme.service';

@Component({
selector: 'app-sidenav-buttons',
templateUrl: './sidenav-buttons.component.html',
styleUrls: ['./sidenav-buttons.component.css'],
})
export class SidenavButtonsComponent {
export class SidenavButtonsComponent implements OnInit {
Options: string[] = [
'Overview',
'Matrix',
Expand Down Expand Up @@ -33,5 +34,19 @@ export class SidenavButtonsComponent {
'/about',
'/userday',
];
constructor() {}

isNightMode = false;

constructor(private themeService: ThemeService) {}

ngOnInit(): void {
const currentTheme = this.themeService.getTheme();
this.isNightMode = currentTheme === 'dark';
}

toggleTheme(): void {
this.isNightMode = !this.isNightMode;
const newTheme = this.isNightMode ? 'dark' : 'light';
this.themeService.setTheme(newTheme);
}
}
1 change: 1 addition & 0 deletions src/app/component/teams/teams.component.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ h3 {
height: 100px;
background-color: #66bb6a;
border-radius: 10px;
color: white;
margin-right: 20px;
display: flex; /* Use flex layout */
justify-content: center; /* Center horizontally */
Expand Down
Loading