From 39c76e98c7ba72451de818ad36cbb8ec3eda5564 Mon Sep 17 00:00:00 2001 From: aguero-tech Date: Tue, 25 Mar 2025 16:21:55 -0500 Subject: [PATCH 01/14] icons for darktheme now matches the green dsomm theme --- .../sidenav-buttons.component.html | 15 ++ .../sidenav-buttons.component.ts | 27 ++- src/custom-theme.scss | 162 ++++++++++++++---- 3 files changed, 171 insertions(+), 33 deletions(-) diff --git a/src/app/component/sidenav-buttons/sidenav-buttons.component.html b/src/app/component/sidenav-buttons/sidenav-buttons.component.html index 2e40515ab..b499a5a13 100644 --- a/src/app/component/sidenav-buttons/sidenav-buttons.component.html +++ b/src/app/component/sidenav-buttons/sidenav-buttons.component.html @@ -1,4 +1,5 @@ + {{ Options[i] }} + + + + + + + + {{ isNightMode ? 'light_mode' : 'dark_mode' }} + +

+ {{ isNightMode ? 'Switch to Light Mode' : 'Switch to Dark Mode' }} +

+
+
diff --git a/src/app/component/sidenav-buttons/sidenav-buttons.component.ts b/src/app/component/sidenav-buttons/sidenav-buttons.component.ts index a48f9703d..0ea11d9b8 100644 --- a/src/app/component/sidenav-buttons/sidenav-buttons.component.ts +++ b/src/app/component/sidenav-buttons/sidenav-buttons.component.ts @@ -1,11 +1,11 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; @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', @@ -33,5 +33,28 @@ export class SidenavButtonsComponent { '/about', '/userday', ]; + + isNightMode = false; + constructor() {} + + ngOnInit(): void { + const themePref = localStorage.getItem('theme'); + this.isNightMode = themePref === 'night'; + this.applyTheme(); + } + + toggleTheme(): void { + this.isNightMode = !this.isNightMode; + this.applyTheme(); + localStorage.setItem('theme', this.isNightMode ? 'night' : 'light'); + } + + private applyTheme(): void { + if (this.isNightMode) { + document.body.classList.add('night-mode'); + } else { + document.body.classList.remove('night-mode'); + } + } } diff --git a/src/custom-theme.scss b/src/custom-theme.scss index 85ffe50ce..babf58089 100644 --- a/src/custom-theme.scss +++ b/src/custom-theme.scss @@ -1,44 +1,144 @@ - -// Custom Theming for Angular Material -// For more information: https://material.angular.io/guide/theming @use '@angular/material' as mat; -// Plus imports for other components in your app. -// Include the common styles for Angular Material. We include this here so that you only -// have to load a single css file for Angular Material in your app. -// Be sure that you only ever include this mixin once! +// ---------------------------------------------- +// Theme Colors and Typography +// ---------------------------------------------- +$light-theme: ( + background: white, + text: black, + link: blue +); -$custom-typography: mat.define-typography-level( - $font-family: montserrat, - $font-weight: 400, - $font-size: 1rem, - $line-height: 1, - $letter-spacing: normal, +$custom-dark-theme: ( + background: #2c2c2c, + text: #e0e0e0, + link: #bb86fc ); +$custom-typography: mat.define-typography-level( + $font-family: montserrat, + $font-weight: 400, + $font-size: 1rem, + $line-height: 1, + $letter-spacing: normal +); @include mat.core($custom-typography); -// Define the palettes for your theme using the Material Design palettes available in palette.scss -// (imported above). For each palette, you can optionally specify a default, lighter, and darker -// hue. Available color palettes: https://material.io/design/color/ -$DSOMM-primary: mat.define-palette(mat.$green-palette,400); +// ---------------------------------------------- +// Angular Material Palettes +// ---------------------------------------------- +$DSOMM-primary: mat.define-palette(mat.$green-palette, 400); $DSOMM-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400); - -// The warn palette is optional (defaults to red). $DSOMM-warn: mat.define-palette(mat.$red-palette); -// Create the theme object. A theme consists of configurations for individual -// theming systems such as "color" or "typography". -$DSOMM-theme: mat.define-light-theme(( - color: ( - primary: $DSOMM-primary, - accent: $DSOMM-accent, - warn: $DSOMM-warn, - ) +// ---------------------------------------------- +// Angular Material Themes +// ---------------------------------------------- +$DSOMM-light-theme: mat.define-light-theme(( + color: ( + primary: $DSOMM-primary, + accent: $DSOMM-accent, + warn: $DSOMM-warn + ) )); -// Include theme styles for core and each component used in your app. -// Alternatively, you can import and @include the theme mixins for each component -// that you are using. -@include mat.all-component-themes($DSOMM-theme); +$DSOMM-dark-theme: mat.define-dark-theme(( + color: ( + primary: $DSOMM-primary, + accent: $DSOMM-accent, + warn: $DSOMM-warn + ) +)); + +// ---------------------------------------------- +// Base Theme Mixin +// ---------------------------------------------- +@mixin apply-theme($theme) { + background-color: map-get($theme, background); + color: map-get($theme, text); + + a { + color: map-get($theme, link); + } +} + +// ---------------------------------------------- +// Light Mode Styles +// ---------------------------------------------- +body { + @include apply-theme($light-theme); + @include mat.all-component-themes($DSOMM-light-theme); + + .title-button, + h1, h2, h3, h4, h5, h6 { + color: map-get($light-theme, text); + } +} + +// ---------------------------------------------- +// Dark Mode Styles +// ---------------------------------------------- +body.night-mode { + @include apply-theme($custom-dark-theme); + @include mat.all-component-themes($DSOMM-dark-theme); + + .title-button, + h1, h2, h3, h4, h5, h6 { + color: map-get($custom-dark-theme, text); + } + + // Common containers + mat-card, + .mat-dialog-container, + .mat-expansion-panel, + .mat-accordion, + .overlay-wrapper { + background-color: #2c2c2c !important; + color: #e0e0e0; + } + + // Dialog styling + .mat-dialog-container { + border: 1px solid #444; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.7); + } + + // Modal override + .overlay-modal { + background-color: #2c2c2c !important; + color: #e0e0e0; + border-radius: 6px; + + mat-card { + background-color: transparent !important; + } + + h1, h2, h3, h4, h5, h6 { + color: #e0e0e0 !important; + } + } + + // Matrix override + .matrix-output p, + .matrix-output span, + .matrix-output div { + color: #e0e0e0; + } + + // Circular heatmap (radar chart) + .circular-heat text, + .labels.segment text { + fill: #ffffff !important; + } + + .circular-heat line, + .circular-heat path { + stroke: #000000; + } +} +.button-container { + display: flex; + flex-direction: column; // Vertical alignment + gap: 10px; // Space between buttons +} \ No newline at end of file From 7a1dab06c96425439dd3eb5f1dd894f5cc2d4679 Mon Sep 17 00:00:00 2001 From: Vegard Bakke Date: Sun, 30 Mar 2025 17:51:58 +0200 Subject: [PATCH 02/14] add dark theme for circular heatmap --- .../circular-heatmap.component.ts | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/app/component/circular-heatmap/circular-heatmap.component.ts b/src/app/component/circular-heatmap/circular-heatmap.component.ts index 4d737b11f..02e710d2e 100644 --- a/src/app/component/circular-heatmap/circular-heatmap.component.ts +++ b/src/app/component/circular-heatmap/circular-heatmap.component.ts @@ -63,6 +63,22 @@ export class CircularHeatmapComponent implements OnInit { showOverlay: boolean; showFilters: boolean; markdown: md = md(); + theme: string; + theme_colors: Record; + themes: Record> = { + 'light': { + background: '#ffffff', + disabled: '#dddddd', + filled: 'green', + cursor: 'green', + }, + 'night': { + background: '#dddddd', + disabled: '#888888', + filled: 'green', + cursor: 'green', + }, + }; constructor( private yaml: ymlService, @@ -71,10 +87,15 @@ export class CircularHeatmapComponent implements OnInit { ) { this.showOverlay = false; this.showFilters = true; + this.theme = 'light'; + this.theme_colors = this.themes[this.theme]; } ngOnInit(): void { console.log(`${this.perfNow()}s: ngOnInit`); + this.theme = localStorage.getItem('theme') || 'light'; + this.theme_colors = this.themes[this.theme]; + // Ensure that Levels and Teams load before MaturityData // using promises, since ngOnInit does not support async/await this.LoadMaturityLevels() @@ -405,7 +426,6 @@ export class CircularHeatmapComponent implements OnInit { .innerRadius(innerRadius) .segmentHeight(segmentHeight) .domain([0, 1]) - .range(['white', 'green']) .radialLabels(radial_labels) .segmentLabels(segment_labels); @@ -498,6 +518,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) { @@ -611,7 +632,7 @@ export class CircularHeatmapComponent implements OnInit { .append('path') .attr('id', 'hover') .attr('pointer-events', 'none') - .attr('stroke', 'green') + .attr('stroke', _self.theme_colors['cursor']) .attr('stroke-width', '7') .attr('fill', 'transparent'); cursors @@ -716,7 +737,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']); } } } @@ -822,7 +843,7 @@ export class CircularHeatmapComponent implements OnInit { var colorSector = d3 .scaleLinear() .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; @@ -833,7 +854,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'] + ); } } } From f9a8d370077765fc7abcc8bfd04340a0740aea81 Mon Sep 17 00:00:00 2001 From: Vegard Bakke Date: Sun, 30 Mar 2025 21:58:34 +0200 Subject: [PATCH 03/14] Giving in to nitpicking linter --- .../component/circular-heatmap/circular-heatmap.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/component/circular-heatmap/circular-heatmap.component.ts b/src/app/component/circular-heatmap/circular-heatmap.component.ts index 02e710d2e..c7ab9df40 100644 --- a/src/app/component/circular-heatmap/circular-heatmap.component.ts +++ b/src/app/component/circular-heatmap/circular-heatmap.component.ts @@ -66,13 +66,13 @@ export class CircularHeatmapComponent implements OnInit { theme: string; theme_colors: Record; themes: Record> = { - 'light': { + light: { background: '#ffffff', disabled: '#dddddd', filled: 'green', cursor: 'green', }, - 'night': { + night: { background: '#dddddd', disabled: '#888888', filled: 'green', From 5256901cd562c9f0be93987bbee9db3f87f0b146 Mon Sep 17 00:00:00 2001 From: Aryan <53595853+0x41head@users.noreply.github.com> Date: Mon, 31 Mar 2025 12:39:34 +0530 Subject: [PATCH 04/14] Update sidenav-buttons.component.spec.ts --- .../component/sidenav-buttons/sidenav-buttons.component.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/component/sidenav-buttons/sidenav-buttons.component.spec.ts b/src/app/component/sidenav-buttons/sidenav-buttons.component.spec.ts index 40ce57642..fd22d9bb9 100644 --- a/src/app/component/sidenav-buttons/sidenav-buttons.component.spec.ts +++ b/src/app/component/sidenav-buttons/sidenav-buttons.component.spec.ts @@ -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); From 3d0aa678b069b4868e8e0355d4883050fb350440 Mon Sep 17 00:00:00 2001 From: aguero-tech Date: Thu, 17 Apr 2025 14:12:22 -0500 Subject: [PATCH 05/14] merged @vbakke's commit and hope to have fixed the theming, all working via side b utton. Will merge PR and then update to DSOMM before contributing. --- src/app/app.component.ts | 5 ++ .../circular-heatmap.component.ts | 52 ++++++++++++++----- .../sidenav-buttons.component.ts | 11 ++-- src/app/service/theme.service.ts | 33 ++++++++++++ src/custom-theme.scss | 34 +++++++++--- src/index.html | 2 +- 6 files changed, 112 insertions(+), 25 deletions(-) create mode 100644 src/app/service/theme.service.ts diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 996312b7c..b0004f02b 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { ThemeService } from './service/theme.service'; @Component({ selector: 'app-root', @@ -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') { diff --git a/src/app/component/circular-heatmap/circular-heatmap.component.ts b/src/app/component/circular-heatmap/circular-heatmap.component.ts index c7ab9df40..a84852e67 100644 --- a/src/app/component/circular-heatmap/circular-heatmap.component.ts +++ b/src/app/component/circular-heatmap/circular-heatmap.component.ts @@ -15,6 +15,7 @@ import { ModalMessageComponent, DialogInfo, } from '../modal-message/modal-message.component'; +import { ThemeService } from '../../service/theme.service'; export interface activitySchema { uuid: string; @@ -83,28 +84,53 @@ export class CircularHeatmapComponent implements OnInit { constructor( private yaml: ymlService, private router: Router, + private themeService: ThemeService, public modal: ModalMessageComponent ) { this.showOverlay = false; this.showFilters = true; this.theme = 'light'; + this.theme = this.themeService.getTheme(); + this.theme_colors = this.themes[this.theme]; } ngOnInit(): void { - console.log(`${this.perfNow()}s: ngOnInit`); - this.theme = localStorage.getItem('theme') || 'light'; - this.theme_colors = this.themes[this.theme]; + 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(), + }; - // 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(); - }); + 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; @@ -569,7 +595,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)); }); diff --git a/src/app/component/sidenav-buttons/sidenav-buttons.component.ts b/src/app/component/sidenav-buttons/sidenav-buttons.component.ts index 0ea11d9b8..a11cd8ccc 100644 --- a/src/app/component/sidenav-buttons/sidenav-buttons.component.ts +++ b/src/app/component/sidenav-buttons/sidenav-buttons.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { ThemeService } from '../../service/theme.service'; @Component({ selector: 'app-sidenav-buttons', @@ -36,18 +37,20 @@ export class SidenavButtonsComponent implements OnInit { isNightMode = false; - constructor() {} + constructor(private themeService: ThemeService) {} ngOnInit(): void { const themePref = localStorage.getItem('theme'); - this.isNightMode = themePref === 'night'; + this.isNightMode = themePref === 'dark'; this.applyTheme(); } toggleTheme(): void { + console.log('[toggleTheme] Triggered'); + this.isNightMode = !this.isNightMode; - this.applyTheme(); - localStorage.setItem('theme', this.isNightMode ? 'night' : 'light'); + const newTheme = this.isNightMode ? 'dark' : 'light'; + this.themeService.setTheme(newTheme); } private applyTheme(): void { diff --git a/src/app/service/theme.service.ts b/src/app/service/theme.service.ts new file mode 100644 index 000000000..a93154d0b --- /dev/null +++ b/src/app/service/theme.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; + +@Injectable({ providedIn: 'root' }) +export class ThemeService { + private themeSubject = new BehaviorSubject('light'); + public readonly theme$ = this.themeSubject.asObservable(); + + constructor() { + const savedTheme = localStorage.getItem('theme') || 'light'; + this.setTheme(savedTheme); + } + + initTheme(): void { + const saved = localStorage.getItem('theme') || 'light'; + this.setTheme(saved); + } + + setTheme(theme: string): void { + document.body.classList.remove('light-theme', 'dark-theme'); + document.body.classList.add(`${theme}-theme`); + + // Force this before other reads + requestAnimationFrame(() => { + this.themeSubject.next(theme); + localStorage.setItem('theme', theme); + }); + } + + getTheme(): string { + return this.themeSubject.value; + } +} diff --git a/src/custom-theme.scss b/src/custom-theme.scss index babf58089..01cebf8e1 100644 --- a/src/custom-theme.scss +++ b/src/custom-theme.scss @@ -66,8 +66,6 @@ $DSOMM-dark-theme: mat.define-dark-theme(( // Light Mode Styles // ---------------------------------------------- body { - @include apply-theme($light-theme); - @include mat.all-component-themes($DSOMM-light-theme); .title-button, h1, h2, h3, h4, h5, h6 { @@ -75,10 +73,20 @@ body { } } +.light-theme { + --heatmap-filled: #4caf50; + --heatmap-disabled: #888888; + --heatmap-cursor: #3f51b5; + --heatmap-background: white; + --heatmap-stroke: black; + + @include mat.all-component-themes($DSOMM-light-theme); +} + // ---------------------------------------------- // Dark Mode Styles // ---------------------------------------------- -body.night-mode { +body.dark-theme { @include apply-theme($custom-dark-theme); @include mat.all-component-themes($DSOMM-dark-theme); @@ -87,6 +95,7 @@ body.night-mode { color: map-get($custom-dark-theme, text); } + // Common containers mat-card, .mat-dialog-container, @@ -126,10 +135,7 @@ body.night-mode { } // Circular heatmap (radar chart) - .circular-heat text, - .labels.segment text { - fill: #ffffff !important; - } + .circular-heat line, .circular-heat path { @@ -137,6 +143,20 @@ body.night-mode { } } +.dark-theme { + + + --heatmap-filled: #4caf50; + --heatmap-disabled: #666666; + --heatmap-cursor: #80deea; + --heatmap-background: #bbbbbb; + --heatmap-stroke: #000000; + + + + @include mat.all-component-themes($DSOMM-dark-theme); +} + .button-container { display: flex; flex-direction: column; // Vertical alignment diff --git a/src/index.html b/src/index.html index 4d624b57e..1a753b50d 100644 --- a/src/index.html +++ b/src/index.html @@ -17,7 +17,7 @@ rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-fork-ribbon-css/0.2.3/gh-fork-ribbon.min.css" /> - + From 94420d8a172f8bdaecbf0713ddc182e539909206 Mon Sep 17 00:00:00 2001 From: aguero-tech Date: Thu, 17 Apr 2025 20:27:59 -0500 Subject: [PATCH 06/14] forgot about the white text in circular heatmap during dark mode --- src/custom-theme.scss | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/custom-theme.scss b/src/custom-theme.scss index 01cebf8e1..75083fe11 100644 --- a/src/custom-theme.scss +++ b/src/custom-theme.scss @@ -93,6 +93,8 @@ body.dark-theme { .title-button, h1, h2, h3, h4, h5, h6 { color: map-get($custom-dark-theme, text); + + } @@ -135,6 +137,10 @@ body.dark-theme { } // Circular heatmap (radar chart) + .circular-heat text, + .labels.segment text { + fill: #ffffff !important; + } .circular-heat line, @@ -144,8 +150,6 @@ body.dark-theme { } .dark-theme { - - --heatmap-filled: #4caf50; --heatmap-disabled: #666666; --heatmap-cursor: #80deea; From f0684c3194429eaf697897abe57ebab70121b876 Mon Sep 17 00:00:00 2001 From: aguero-tech Date: Wed, 23 Apr 2025 10:44:28 -0500 Subject: [PATCH 07/14] dark theme color improvements; scss improvements, removed hardcoded colors. Incorporate4d lots of vbakke's requests (cleaning up code). Fixed the persistence of theme after refresh. --- .../circular-heatmap.component.ts | 29 +-------- .../sidenav-buttons.component.ts | 17 +----- src/app/service/theme.service.ts | 13 +--- src/custom-theme.scss | 61 +++++++++++++++---- src/index.html | 2 +- src/main.ts | 5 ++ 6 files changed, 63 insertions(+), 64 deletions(-) diff --git a/src/app/component/circular-heatmap/circular-heatmap.component.ts b/src/app/component/circular-heatmap/circular-heatmap.component.ts index a84852e67..99d2571d4 100644 --- a/src/app/component/circular-heatmap/circular-heatmap.component.ts +++ b/src/app/component/circular-heatmap/circular-heatmap.component.ts @@ -65,21 +65,7 @@ export class CircularHeatmapComponent implements OnInit { showFilters: boolean; markdown: md = md(); theme: string; - theme_colors: Record; - themes: Record> = { - light: { - background: '#ffffff', - disabled: '#dddddd', - filled: 'green', - cursor: 'green', - }, - night: { - background: '#dddddd', - disabled: '#888888', - filled: 'green', - cursor: 'green', - }, - }; + theme_colors!: Record; constructor( private yaml: ymlService, @@ -89,10 +75,7 @@ export class CircularHeatmapComponent implements OnInit { ) { this.showOverlay = false; this.showFilters = true; - this.theme = 'light'; this.theme = this.themeService.getTheme(); - - this.theme_colors = this.themes[this.theme]; } ngOnInit(): void { @@ -657,17 +640,11 @@ export class CircularHeatmapComponent implements OnInit { cursors .append('path') .attr('id', 'hover') - .attr('pointer-events', 'none') - .attr('stroke', _self.theme_colors['cursor']) - .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'); }); } diff --git a/src/app/component/sidenav-buttons/sidenav-buttons.component.ts b/src/app/component/sidenav-buttons/sidenav-buttons.component.ts index a11cd8ccc..237001ac4 100644 --- a/src/app/component/sidenav-buttons/sidenav-buttons.component.ts +++ b/src/app/component/sidenav-buttons/sidenav-buttons.component.ts @@ -40,24 +40,13 @@ export class SidenavButtonsComponent implements OnInit { constructor(private themeService: ThemeService) {} ngOnInit(): void { - const themePref = localStorage.getItem('theme'); - this.isNightMode = themePref === 'dark'; - this.applyTheme(); + const currentTheme = this.themeService.getTheme(); + this.isNightMode = currentTheme === 'dark'; } toggleTheme(): void { - console.log('[toggleTheme] Triggered'); - this.isNightMode = !this.isNightMode; const newTheme = this.isNightMode ? 'dark' : 'light'; this.themeService.setTheme(newTheme); } - - private applyTheme(): void { - if (this.isNightMode) { - document.body.classList.add('night-mode'); - } else { - document.body.classList.remove('night-mode'); - } - } -} +} \ No newline at end of file diff --git a/src/app/service/theme.service.ts b/src/app/service/theme.service.ts index a93154d0b..810b76539 100644 --- a/src/app/service/theme.service.ts +++ b/src/app/service/theme.service.ts @@ -6,10 +6,7 @@ export class ThemeService { private themeSubject = new BehaviorSubject('light'); public readonly theme$ = this.themeSubject.asObservable(); - constructor() { - const savedTheme = localStorage.getItem('theme') || 'light'; - this.setTheme(savedTheme); - } + constructor() {} initTheme(): void { const saved = localStorage.getItem('theme') || 'light'; @@ -19,12 +16,8 @@ export class ThemeService { setTheme(theme: string): void { document.body.classList.remove('light-theme', 'dark-theme'); document.body.classList.add(`${theme}-theme`); - - // Force this before other reads - requestAnimationFrame(() => { - this.themeSubject.next(theme); - localStorage.setItem('theme', theme); - }); + localStorage.setItem('theme', theme); + this.themeSubject.next(theme); } getTheme(): string { diff --git a/src/custom-theme.scss b/src/custom-theme.scss index 75083fe11..2438398cf 100644 --- a/src/custom-theme.scss +++ b/src/custom-theme.scss @@ -75,10 +75,14 @@ body { .light-theme { --heatmap-filled: #4caf50; - --heatmap-disabled: #888888; - --heatmap-cursor: #3f51b5; + --heatmap-disabled: #dddddd; + --heatmap-cursor: green; --heatmap-background: white; --heatmap-stroke: black; + --heatmap-cursor-selected:var(--heatmap-cursor); + --heatmap-cursor-hover: transparent; + + @include mat.all-component-themes($DSOMM-light-theme); } @@ -129,12 +133,6 @@ body.dark-theme { } } - // Matrix override - .matrix-output p, - .matrix-output span, - .matrix-output div { - color: #e0e0e0; - } // Circular heatmap (radar chart) .circular-heat text, @@ -145,24 +143,61 @@ body.dark-theme { .circular-heat line, .circular-heat path { - stroke: #000000; + stroke: var(--heatmap-stroke); } } -.dark-theme { +body.dark-theme { --heatmap-filled: #4caf50; --heatmap-disabled: #666666; - --heatmap-cursor: #80deea; + --heatmap-cursor: green; --heatmap-background: #bbbbbb; --heatmap-stroke: #000000; + --heatmap-cursor-selected:var(--heatmap-cursor); + --heatmap-cursor-hover: transparent; + app-matrix a:visited { + color: #d4baf4; + } + app-matrix .tags-activity, + app-matrix .tags-activity span { + color: #397af4; + } - @include mat.all-component-themes($DSOMM-dark-theme); + .mat-chip.mat-standard-chip { + color: #ababab; + } + + .mat-chip.mat-standard-chip.mat-chip-selected.mat-primary { + background-color: #74b277; + } } + + + + @include mat.all-component-themes($DSOMM-dark-theme); + + .button-container { display: flex; flex-direction: column; // Vertical alignment gap: 10px; // Space between buttons -} \ No newline at end of file +} + +svg .cursors path { + fill: transparent; + pointer-events: none; +} + +svg .cursors #hover { + stroke: var(--heatmap-cursor); + stroke-width: 7px; +} + +svg .cursors #selected { + stroke: var(--heatmap-cursor-selected, #000000); // optional fallback + stroke-width: 7px; +} + diff --git a/src/index.html b/src/index.html index 1a753b50d..8d114a99a 100644 --- a/src/index.html +++ b/src/index.html @@ -17,7 +17,7 @@ rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-fork-ribbon-css/0.2.3/gh-fork-ribbon.min.css" /> - + diff --git a/src/main.ts b/src/main.ts index fa4e0aef3..345453bdf 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,3 +1,8 @@ +const savedTheme = localStorage.getItem('theme') || 'light'; +document.body.classList.remove('light-theme', 'dark-theme'); +document.body.classList.add(`${savedTheme}-theme`); +console.log('[main.ts] Theme set to:', savedTheme); // + import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; From a7d8cc1b6866a8b6154ec5d8bf53651fdc15b02d Mon Sep 17 00:00:00 2001 From: aguero-tech Date: Wed, 23 Apr 2025 10:58:56 -0500 Subject: [PATCH 08/14] giving into a return for prettier eslint --- src/app/component/sidenav-buttons/sidenav-buttons.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/component/sidenav-buttons/sidenav-buttons.component.ts b/src/app/component/sidenav-buttons/sidenav-buttons.component.ts index 237001ac4..53073eb35 100644 --- a/src/app/component/sidenav-buttons/sidenav-buttons.component.ts +++ b/src/app/component/sidenav-buttons/sidenav-buttons.component.ts @@ -49,4 +49,4 @@ export class SidenavButtonsComponent implements OnInit { const newTheme = this.isNightMode ? 'dark' : 'light'; this.themeService.setTheme(newTheme); } -} \ No newline at end of file +} From c3fb90894a61aaae8b0fee639b95b39d048b3653 Mon Sep 17 00:00:00 2001 From: aguero-tech Date: Wed, 30 Apr 2025 13:44:54 -0500 Subject: [PATCH 09/14] Adding general properties for text in dark theme. (custome-theme.scss online 106). (Line-63; updating link colors to determine by browser visited state for dark theme. (Line-159; moving app-matrix to respective componenet.css and fix for visited. Fix on styles.css for dark theme on About Us. --- .../circular-heatmap.component.css | 5 ++-- src/app/component/matrix/matrix.component.css | 13 +++++++- src/app/component/teams/teams.component.css | 1 + src/custom-theme.scss | 30 +++++++++---------- src/styles.css | 7 +++++ 5 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/app/component/circular-heatmap/circular-heatmap.component.css b/src/app/component/circular-heatmap/circular-heatmap.component.css index 2056fa017..4dd5b7c35 100644 --- a/src/app/component/circular-heatmap/circular-heatmap.component.css +++ b/src/app/component/circular-heatmap/circular-heatmap.component.css @@ -78,7 +78,8 @@ grid-column: 2/3; grid-row: 1/4; display: grid; - justify-content: top; + justify-content: center; + align-items: start; margin-left: auto; } .team-filter { @@ -161,6 +162,6 @@ button.filter-toggle { .overlay-details { width: 100%; - } + } } \ No newline at end of file diff --git a/src/app/component/matrix/matrix.component.css b/src/app/component/matrix/matrix.component.css index 34c39e3dd..7351c6508 100644 --- a/src/app/component/matrix/matrix.component.css +++ b/src/app/component/matrix/matrix.component.css @@ -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; diff --git a/src/app/component/teams/teams.component.css b/src/app/component/teams/teams.component.css index f233fb6c4..ae7eccd15 100644 --- a/src/app/component/teams/teams.component.css +++ b/src/app/component/teams/teams.component.css @@ -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 */ diff --git a/src/custom-theme.scss b/src/custom-theme.scss index 2438398cf..f482bd461 100644 --- a/src/custom-theme.scss +++ b/src/custom-theme.scss @@ -6,13 +6,13 @@ $light-theme: ( background: white, text: black, - link: blue + link: blue, ); $custom-dark-theme: ( background: #2c2c2c, text: #e0e0e0, - link: #bb86fc + link: #bb86fc, ); $custom-typography: mat.define-typography-level( @@ -60,6 +60,9 @@ $DSOMM-dark-theme: mat.define-dark-theme(( a { color: map-get($theme, link); } + a:visited { + color: map-get($theme, visited-link); + } } // ---------------------------------------------- @@ -100,7 +103,10 @@ body.dark-theme { } - + // General properties + p, li, tr { + color: #e0e0e0; + } // Common containers mat-card, @@ -108,7 +114,7 @@ body.dark-theme { .mat-expansion-panel, .mat-accordion, .overlay-wrapper { - background-color: #2c2c2c !important; + background-color: #2c2c2c; color: #e0e0e0; } @@ -120,16 +126,16 @@ body.dark-theme { // Modal override .overlay-modal { - background-color: #2c2c2c !important; + background-color: #2c2c2c; color: #e0e0e0; border-radius: 6px; mat-card { - background-color: transparent !important; + background-color: transparent; } h1, h2, h3, h4, h5, h6 { - color: #e0e0e0 !important; + color: #e0e0e0; } } @@ -137,7 +143,7 @@ body.dark-theme { // Circular heatmap (radar chart) .circular-heat text, .labels.segment text { - fill: #ffffff !important; + fill: #ffffff; } @@ -156,14 +162,6 @@ body.dark-theme { --heatmap-cursor-selected:var(--heatmap-cursor); --heatmap-cursor-hover: transparent; - app-matrix a:visited { - color: #d4baf4; - } - - app-matrix .tags-activity, - app-matrix .tags-activity span { - color: #397af4; - } .mat-chip.mat-standard-chip { color: #ababab; diff --git a/src/styles.css b/src/styles.css index 3f9148dbb..e1b4d99e7 100644 --- a/src/styles.css +++ b/src/styles.css @@ -8,6 +8,13 @@ body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } padding: 0.3em; } +.dark-theme .userday table :is(td, th) { + border-color: #e0e0e0; +} +.dark-theme .userday tr:nth-child(even) { + background-color: #365d36; +} + .userday tr:nth-child(even) { background-color: #66bb6a; } From fe7fd67fef248926835bf055040c90614a3da576 Mon Sep 17 00:00:00 2001 From: aguero-tech Date: Wed, 30 Apr 2025 13:49:47 -0500 Subject: [PATCH 10/14] Adding general properties for text in dark theme. (custome-theme.scss online 106). (Line-63; updating link colors to determine by browser visited state for dark theme. (Line-159; moving app-matrix to respective componenet.css and fix for visited. Fix on styles.css for dark theme on About Us. --- .../component/circular-heatmap/circular-heatmap.component.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/component/circular-heatmap/circular-heatmap.component.css b/src/app/component/circular-heatmap/circular-heatmap.component.css index 4dd5b7c35..bd300f384 100644 --- a/src/app/component/circular-heatmap/circular-heatmap.component.css +++ b/src/app/component/circular-heatmap/circular-heatmap.component.css @@ -73,7 +73,6 @@ .overlay-close { border: black solid 1px; background-color: rgba(0, 0, 0, 0); - border: none; color: black; grid-column: 2/3; grid-row: 1/4; From 48f1a529a74db61f1ff3331dc2d1976122e8db40 Mon Sep 17 00:00:00 2001 From: aguero-tech Date: Mon, 5 May 2025 12:19:53 -0500 Subject: [PATCH 11/14] removing the border on the close dialog box and updating the "x" to adapt to dark/light theme --- .../circular-heatmap.component.css | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/app/component/circular-heatmap/circular-heatmap.component.css b/src/app/component/circular-heatmap/circular-heatmap.component.css index bd300f384..51b43e55d 100644 --- a/src/app/component/circular-heatmap/circular-heatmap.component.css +++ b/src/app/component/circular-heatmap/circular-heatmap.component.css @@ -71,9 +71,8 @@ } .overlay-close { - border: black solid 1px; + border: none; background-color: rgba(0, 0, 0, 0); - color: black; grid-column: 2/3; grid-row: 1/4; display: grid; @@ -81,6 +80,17 @@ 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; From e035bb3edcf1c848bebfed88fff80d0406e75d71 Mon Sep 17 00:00:00 2001 From: aguero-tech Date: Mon, 5 May 2025 12:27:13 -0500 Subject: [PATCH 12/14] darkened the filled green in heatmap when in dark theme --- src/custom-theme.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/custom-theme.scss b/src/custom-theme.scss index f482bd461..39d2dea7e 100644 --- a/src/custom-theme.scss +++ b/src/custom-theme.scss @@ -154,7 +154,7 @@ body.dark-theme { } body.dark-theme { - --heatmap-filled: #4caf50; + --heatmap-filled: #007700; --heatmap-disabled: #666666; --heatmap-cursor: green; --heatmap-background: #bbbbbb; From a961553d008ab2efacd58393919f4c6d6a857c97 Mon Sep 17 00:00:00 2001 From: aguero-tech Date: Thu, 8 May 2025 12:10:06 -0500 Subject: [PATCH 13/14] final reformatting and improved body.dark-theme nesting in scss; also fixed my GPG key signature for contribution - Manny --- src/custom-theme.scss | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/src/custom-theme.scss b/src/custom-theme.scss index 39d2dea7e..d7d437a97 100644 --- a/src/custom-theme.scss +++ b/src/custom-theme.scss @@ -85,8 +85,6 @@ body { --heatmap-cursor-selected:var(--heatmap-cursor); --heatmap-cursor-hover: transparent; - - @include mat.all-component-themes($DSOMM-light-theme); } @@ -97,12 +95,19 @@ body.dark-theme { @include apply-theme($custom-dark-theme); @include mat.all-component-themes($DSOMM-dark-theme); + --heatmap-filled: #007700; + --heatmap-disabled: #666666; + --heatmap-cursor: green; + --heatmap-background: #bbbbbb; + --heatmap-stroke: #000000; + --heatmap-cursor-selected:var(--heatmap-cursor); + --heatmap-cursor-hover: transparent; + .title-button, h1, h2, h3, h4, h5, h6 { color: map-get($custom-dark-theme, text); - - } + // General properties p, li, tr { color: #e0e0e0; @@ -139,29 +144,16 @@ body.dark-theme { } } - // Circular heatmap (radar chart) .circular-heat text, .labels.segment text { fill: #ffffff; } - .circular-heat line, .circular-heat path { stroke: var(--heatmap-stroke); } -} - -body.dark-theme { - --heatmap-filled: #007700; - --heatmap-disabled: #666666; - --heatmap-cursor: green; - --heatmap-background: #bbbbbb; - --heatmap-stroke: #000000; - --heatmap-cursor-selected:var(--heatmap-cursor); - --heatmap-cursor-hover: transparent; - .mat-chip.mat-standard-chip { color: #ababab; @@ -172,11 +164,7 @@ body.dark-theme { } } - - - - @include mat.all-component-themes($DSOMM-dark-theme); - +@include mat.all-component-themes($DSOMM-dark-theme); .button-container { display: flex; @@ -198,4 +186,3 @@ svg .cursors #selected { stroke: var(--heatmap-cursor-selected, #000000); // optional fallback stroke-width: 7px; } - From 1277ef332e766d551b16d0369f5e7232bda758d0 Mon Sep 17 00:00:00 2001 From: aguero-tech Date: Thu, 8 May 2025 15:10:53 -0500 Subject: [PATCH 14/14] changed the dependency graph node from yellow to #666666 when in dark mode. Also subscribed the dependency graph text to theme.service to dynamically change text color w/o refresh. Basic Linting. --- .../dependency-graph.component.ts | 48 ++++++++++++++++--- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/src/app/component/dependency-graph/dependency-graph.component.ts b/src/app/component/dependency-graph/dependency-graph.component.ts index b1e253220..4990ae632 100644 --- a/src/app/component/dependency-graph/dependency-graph.component.ts +++ b/src/app/component/dependency-graph/dependency-graph.component.ts @@ -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; @@ -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: [] }; @@ -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'); @@ -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') @@ -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']);