diff --git a/src/app/app.component.html b/src/app/app.component.html index b86ef90e22afcc573488a94b2a31092e5d3f795c..86bb7600d61f42f1505970e6eae03a0aa6a93618 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,7 +1,7 @@ -<div fxLayout="column" fxFill> - <app-header></app-header> - <div fxFlex class="app-component"> - <router-outlet></router-outlet> + <div [ngClass]="header.themeClass" fxLayout="column" fxFill appTheme> + <app-header #header></app-header> + <div fxFlex class="app-component"> + <router-outlet></router-outlet> + </div> + <app-footer></app-footer> </div> - <app-footer></app-footer> -</div> diff --git a/src/app/app.component.scss b/src/app/app.component.scss index 23f30f8823f78bc2ff012e22dfa0f15a82ec9f7a..526e030fe8b95bd6765530defef873a9e9348b82 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -2,7 +2,7 @@ .app-component { padding: 4%; - background-color: #b2dfdb; + background-color:var(--background-color); } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index aae1fa0a69aeddecb214bb15270b559cdce4dea5..39c8d57d7b6e576f503beaa91ccd1d6b777e7f7e 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; +import { ThemeService } from '../theme/theme.service'; @Component({ selector: 'app-root', @@ -8,7 +9,8 @@ import { TranslateService } from '@ngx-translate/core'; }) export class AppComponent { - constructor(private translationService: TranslateService) { + constructor(private translationService: TranslateService, + private themeService: ThemeService) { translationService.setDefaultLang(this.translationService.getBrowserLang()); sessionStorage.setItem('currentLang', this.translationService.getBrowserLang()); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 83b65385dbc7cf3d591a5a92213add4853795dae..30c5ab13ffaff929b5963d9c28c31b7c37ce4af3 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -26,6 +26,7 @@ import { MarkdownService, MarkedOptions } from 'ngx-markdown'; import { NewLandingComponent } from './components/home/new-landing/new-landing.component'; import { HomePageComponent } from './components/home/home-page/home-page.component'; import { AppConfig } from './app.config'; +import { ThemeModule } from '../theme/theme.module'; export function dialogClose(dialogResult: any) { } @@ -53,7 +54,8 @@ export function initializeApp(appConfig: AppConfig) { BrowserModule, BrowserAnimationsModule, EssentialsModule, - SharedModule + SharedModule, + ThemeModule ], providers: [ AppConfig, diff --git a/src/app/components/shared/header/header.component.html b/src/app/components/shared/header/header.component.html index ad321a5fe44d21a6d4337bdceb91751121a81ad7..bcaf1c673d5cc226ea12c326e2efd7556a54e96c 100644 --- a/src/app/components/shared/header/header.component.html +++ b/src/app/components/shared/header/header.component.html @@ -7,6 +7,16 @@ <span *ngIf="router.url !== '/home'" class="app-title" (click)="goToHomepage()">ARSnova</span> <span class="fill-remaining-space"></span> + + <mat-menu #themeMenu="matMenu" [overlapTrigger]="false"> + <button mat-menu-item (click)="changeTheme('')">{{ 'default' | translate }}</button> + <button mat-menu-item (click)="changeTheme('dark')">{{ 'dark' | translate }}</button> + </mat-menu> + + <button mat-icon-button [matMenuTriggerFor]="themeMenu"> + <mat-icon>palette</mat-icon> + </button> + <mat-menu #langMenu="matMenu" [overlapTrigger]="false"> <button mat-menu-item (click)="useLanguage('de')">{{ 'header.german' | translate }}</button> <button mat-menu-item (click)="useLanguage('en')">{{ 'header.english' | translate }}</button> diff --git a/src/app/components/shared/header/header.component.ts b/src/app/components/shared/header/header.component.ts index b3bb28fb8b2d72159f3b96d4ca444239d73bbfcc..1ba5921eb3359ea24bc7be9f1665b8a392882c97 100644 --- a/src/app/components/shared/header/header.component.ts +++ b/src/app/components/shared/header/header.component.ts @@ -9,6 +9,7 @@ import { TranslateService } from '@ngx-translate/core'; import { LanguageService } from '../../../services/util/language.service'; import { MatDialog } from '@angular/material'; import { LoginComponent } from '../login/login.component'; +import { ThemeService } from '../../../../theme/theme.service'; @Component({ selector: 'app-header', @@ -17,6 +18,7 @@ import { LoginComponent } from '../login/login.component'; }) export class HeaderComponent implements OnInit { user: User; + themeClass = localStorage.getItem('classNameOfTheme'); constructor(public location: Location, private authenticationService: AuthenticationService, @@ -24,7 +26,9 @@ export class HeaderComponent implements OnInit { public router: Router, private translationService: TranslateService, private langService: LanguageService, - public dialog: MatDialog) { + public dialog: MatDialog, + private themeService: ThemeService + ) { } ngOnInit() { @@ -61,6 +65,16 @@ export class HeaderComponent implements OnInit { this.langService.langEmitter.emit(language); } + changeTheme(theme) { + this.themeClass = theme; + localStorage.setItem('classNameOfTheme', theme); + if (theme === '') { + this.themeService.setActiveThem('arsnovaTheme'); + } else { + this.themeService.setActiveThem(theme); + } + } + login(isDozent: boolean) { const dialogRef = this.dialog.open(LoginComponent, { width: '350px' diff --git a/src/styles.scss b/src/styles.scss index 7186b2044d95fd612daac9f466ab94aa6c52e436..fdfdd98102103ea266215dfaba4df0919940c8f5 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1,4 +1,5 @@ @import 'theme/_variables.scss'; +@import "theme/_dark-theme.scss"; // Plus imports for other components in your app. // Include the common styles for Angular Material. We include this here so that you only @@ -11,4 +12,10 @@ // that you are using. @include angular-material-theme($arsnova-theme); + +.dark { + @include angular-material-theme($dark-theme); +} + + @import 'theme/_theme.scss'; diff --git a/src/theme/_dark-theme.scss b/src/theme/_dark-theme.scss new file mode 100644 index 0000000000000000000000000000000000000000..b633de8a81c12697f289ef5c7428472586bca88f --- /dev/null +++ b/src/theme/_dark-theme.scss @@ -0,0 +1,39 @@ +@import '~@angular/material/theming'; + +html, body { + font-family: 'Roboto', 'Helvetica Neue', sans-serif; + margin: 0; + padding: 0; + width: 100%; + height: 100%; +} + +.fill-remaining-space { + /* This fills the remaining space, by using flexbox. + Every toolbar row uses a flexbox row layout. */ + flex: 1 1 auto; +} + +mat-form-field.input-block { + display: block; +} + +.mat-fab .mat-button-wrapper { + padding: 0!important; +} + +.mat-dialog-container { + background-color: #e0f2f1; +} + +.mat-tab-header { + border-bottom-style: none!important; +} + + +$dark-primary: mat-palette($mat-gray); +$dark-accent: mat-palette($mat-blue-gray); +$dark-warn: mat-palette($mat-red); + +$dark-theme: mat-dark-theme($dark-primary, $dark-accent, $dark-warn); + diff --git a/src/theme/_theme.scss b/src/theme/_theme.scss index 24c076011434ae638509272fec39a677bb7ed860..33dd1ada54fba4bfba27511cc76058672cbca1ae 100644 --- a/src/theme/_theme.scss +++ b/src/theme/_theme.scss @@ -11,10 +11,3 @@ html, body { @import '_util.scss'; @import '_form.scss'; -.progress-theme { - $progress-primary: mat-palette($mat-light-green, 300); - $progress-accent: mat-palette($mat-amber, 300); - $progress-warn: mat-palette($mat-deep-orange, 300); - $progress-theme: mat-light-theme($progress-primary, $progress-accent, $progress-warn); - @include angular-material-theme($progress-theme); -} diff --git a/src/theme/_variables.scss b/src/theme/_variables.scss index a4a274efc2e5a2aafd32c402519f349708e76619..18e71b8b2004a2f2b4f90127ef90feebf9eacfc9 100644 --- a/src/theme/_variables.scss +++ b/src/theme/_variables.scss @@ -10,3 +10,4 @@ $arsnova-warn: mat-palette($mat-red, A100); // Create the theme object (a Sass map containing all of the palettes). $arsnova-theme: mat-light-theme($arsnova-primary, $arsnova-accent, $arsnova-warn); + diff --git a/src/theme/arsnova-theme.const.ts b/src/theme/arsnova-theme.const.ts new file mode 100644 index 0000000000000000000000000000000000000000..311968dcc98fbb9acfde7fc4e90e31ff8836ed35 --- /dev/null +++ b/src/theme/arsnova-theme.const.ts @@ -0,0 +1,12 @@ +export const themes = { + arsnovaTheme: { + '--button-color': '#80cbc4', + '--background-color': '#b2dfdb', + '--black-color' : '#000000' + }, + dark: { + '--button-color': '#80cbc4', + '--background-color': '#000000', + '--black-color' : '#000000' + } +}; diff --git a/src/theme/theme.directive.ts b/src/theme/theme.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..41a493da22e7284bc882e9fc6285ac0c9b217db4 --- /dev/null +++ b/src/theme/theme.directive.ts @@ -0,0 +1,46 @@ +import { Directive, ElementRef, Inject, OnDestroy, OnInit, Renderer2 } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; +import { themes } from './arsnova-theme.const'; +import { ThemeService } from './theme.service'; +import { Subscription } from 'rxjs'; + +@Directive({ + selector: '[appTheme]' +}) + +export class ThemeDirective implements OnInit, OnDestroy { + + private themeName = 'arsnovaTheme'; + private themServiceSubscription: Subscription; + + constructor(private elementRef: ElementRef, + private renderer: Renderer2, + @Inject(DOCUMENT) private document: any, + private themService: ThemeService) { + } + + ngOnInit() { + this.updateTheme(this.themeName); + this.themService.getActiveTheme() + .subscribe(themeName => { + this.themeName = themeName; + this.updateTheme(this.themeName); + }); + } + + updateTheme(themeName: string) { + const them = themes[ themeName ]; + for (const key in them) { + if (them.hasOwnProperty(key)) { + this.renderer.setProperty(this.elementRef.nativeElement, key, them[key]); + this.document.body.style.setProperty(key, them[key]); + } + } + } + + ngOnDestroy() { + if (this.themServiceSubscription) { + this.themServiceSubscription.unsubscribe(); + } + } +} diff --git a/src/theme/theme.module.ts b/src/theme/theme.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..af6d3e09bbdfe23c709e90d26264f87d749fb2a1 --- /dev/null +++ b/src/theme/theme.module.ts @@ -0,0 +1,14 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { ThemeDirective } from './theme.directive'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule + ], + declarations: [ThemeDirective], + exports: [ThemeDirective], +}) +export class ThemeModule { } diff --git a/src/theme/theme.service.ts b/src/theme/theme.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..9c56dfb379aacc5f0df11e7ea62560038d9838e3 --- /dev/null +++ b/src/theme/theme.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class ThemeService { + themeName = localStorage.getItem('classNameOfTheme'); + private activeThem = new BehaviorSubject(this.themeName); + + constructor() { } + + public getActiveTheme() { + return this.activeThem.asObservable(); + } + + public setActiveThem(name) { + this.activeThem.next(name); + } +}