mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-02-01 00:45:53 +00:00
Merge pull request #626 from GNS3/white-theme
Light theme (white) support
This commit is contained in:
commit
8f4b2c502f
12
angular.json
12
angular.json
@ -16,6 +16,7 @@
|
||||
"main": "src/main.ts",
|
||||
"tsConfig": "src/tsconfig.app.json",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"extractCss": true,
|
||||
"assets": [
|
||||
"src/assets",
|
||||
"src/favicon.ico",
|
||||
@ -25,7 +26,16 @@
|
||||
"node_modules/bootstrap/dist/css/bootstrap.min.css",
|
||||
"node_modules/notosans-fontface/css/notosans-fontface.min.css",
|
||||
"src/styles.css",
|
||||
"src/theme.scss"
|
||||
{
|
||||
"input": "src/theme.scss",
|
||||
"lazy": true,
|
||||
"bundleName": "theme-default-dark"
|
||||
},
|
||||
{
|
||||
"input": "src/theme-light.scss",
|
||||
"lazy": true,
|
||||
"bundleName": "theme-default"
|
||||
}
|
||||
],
|
||||
"scripts": []
|
||||
},
|
||||
|
@ -3,6 +3,7 @@ import { MatIconRegistry } from '@angular/material';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { ElectronService } from 'ngx-electron';
|
||||
import { SettingsService } from './services/settings.service';
|
||||
import { ThemeService } from './services/theme.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@ -14,9 +15,11 @@ export class AppComponent implements OnInit {
|
||||
iconReg: MatIconRegistry,
|
||||
sanitizer: DomSanitizer,
|
||||
private settingsService: SettingsService,
|
||||
private electronService: ElectronService
|
||||
private electronService: ElectronService,
|
||||
private themeService: ThemeService
|
||||
) {
|
||||
iconReg.addSvgIcon('gns3', sanitizer.bypassSecurityTrustResourceUrl('./assets/gns3_icon.svg'));
|
||||
iconReg.addSvgIcon('gns3black', sanitizer.bypassSecurityTrustResourceUrl('./assets/gns3_icon_black.svg'));
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
@ -25,5 +28,11 @@ export class AppComponent implements OnInit {
|
||||
this.electronService.ipcRenderer.send('settings.changed', settings);
|
||||
});
|
||||
}
|
||||
let theme = localStorage.getItem('theme');
|
||||
if (theme === 'light') {
|
||||
this.themeService.setDarkMode(false);
|
||||
} else {
|
||||
this.themeService.setDarkMode(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -255,6 +255,7 @@ import { DeviceDetectorModule } from 'ngx-device-detector';
|
||||
import { ConfigDialogComponent } from './components/project-map/context-menu/dialogs/config-dialog/config-dialog.component';
|
||||
import { Gns3vmComponent } from './components/preferences/gns3vm/gns3vm.component';
|
||||
import { Gns3vmService } from './services/gns3vm.service';
|
||||
import { ThemeService } from './services/theme.service';
|
||||
|
||||
if (environment.production) {
|
||||
Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
|
||||
@ -516,7 +517,8 @@ if (environment.production) {
|
||||
TracengService,
|
||||
PacketCaptureService,
|
||||
NotificationService,
|
||||
Gns3vmService
|
||||
Gns3vmService,
|
||||
ThemeService
|
||||
],
|
||||
entryComponents: [
|
||||
AddServerDialogComponent,
|
||||
|
@ -1,5 +1,5 @@
|
||||
.ad {
|
||||
background-color: #263238;
|
||||
background-color: transparent;
|
||||
width: 400px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="notification-box" [ngClass]="{hidden: !isVisible}">
|
||||
<mat-progress-bar mode="determinate" [value]="progress"></mat-progress-bar>
|
||||
<div style="display: flex; height: 102px;">
|
||||
<div class="content">
|
||||
<div class="content" [ngClass]="{lightTheme: isLightThemeEnabled}">
|
||||
<app-adbutler (onLoad)="onLoadingAdbutler($event)" theme="dark"></app-adbutler>
|
||||
<mat-icon (click)="closeNotification()" class="close-button">close</mat-icon>
|
||||
</div>
|
||||
|
@ -14,6 +14,10 @@
|
||||
border-bottom: 2px solid #0097a7;
|
||||
}
|
||||
|
||||
.lightTheme {
|
||||
background-color: white!important;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
position: fixed;
|
||||
bottom: 90px;
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { timer, Observable, Subscription } from 'rxjs';
|
||||
import { ThemeService } from '../../services/theme.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-notification-box',
|
||||
@ -22,11 +23,15 @@ export class NotificationBoxComponent implements OnInit, OnDestroy {
|
||||
breakTime: number = 20;
|
||||
isEndless: boolean = false;
|
||||
numberOfViews: number = 1;
|
||||
isLightThemeEnabled: boolean = false;
|
||||
|
||||
constructor(){}
|
||||
constructor(
|
||||
private themeService: ThemeService
|
||||
){}
|
||||
|
||||
ngOnInit() {
|
||||
this.startTimer();
|
||||
this.themeService.getActualTheme() === 'light' ? this.isLightThemeEnabled = true : this.isLightThemeEnabled = false;
|
||||
}
|
||||
|
||||
startTimer() {
|
||||
|
@ -5,6 +5,7 @@
|
||||
</div>
|
||||
<div
|
||||
class="consoleWrapper"
|
||||
[ngClass]="{lightTheme: isLightThemeEnabled}"
|
||||
(mousedown)="toggleDragging(true)"
|
||||
[ngStyle]="style"
|
||||
mwlResizable
|
||||
@ -15,7 +16,7 @@
|
||||
(resizeEnd)="onResizeEnd($event)">
|
||||
<div class="consoleHeader">
|
||||
<div class="consoleFiltering">
|
||||
<button class="filterButton" [matMenuTriggerFor]="filterMenu">
|
||||
<button [ngClass]="{lightTheme: isLightThemeEnabled}" class="filterButton" [matMenuTriggerFor]="filterMenu">
|
||||
Apply filter
|
||||
</button>
|
||||
<mat-menu #filterMenu="matMenu" xPosition="after">
|
||||
@ -33,15 +34,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div #console class="console" [ngStyle]="styleInside">
|
||||
<div [ngClass]="{lightTheme: isLightThemeEnabled}" #console class="console" [ngStyle]="styleInside">
|
||||
<span class="console-item" *ngFor="let event of filteredEvents">
|
||||
{{event.message}} <br/>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="consoleInput">
|
||||
<div [ngClass]="{lightTheme: isLightThemeEnabled}" class="consoleInput">
|
||||
<mat-icon class="inputIcon">keyboard_arrow_right</mat-icon>
|
||||
<input
|
||||
[ngClass]="{lightTheme: isLightThemeEnabled}"
|
||||
class="commandLine"
|
||||
autofocus
|
||||
(keydown)="onKeyDown($event)"
|
||||
|
@ -11,8 +11,13 @@
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.lightTheme {
|
||||
background: white!important;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.filterButton {
|
||||
background: #263238;
|
||||
background: transparent;
|
||||
color: white;
|
||||
border: none;
|
||||
margin-top: 0px;
|
||||
@ -54,7 +59,7 @@
|
||||
}
|
||||
|
||||
.commandLine {
|
||||
background-color: #263238;
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import { LogEventsDataSource } from './log-events-datasource';
|
||||
import { HttpServer } from '../../../services/http-server.service';
|
||||
import { LogEvent } from '../../../models/logEvent';
|
||||
import { ResizeEvent } from 'angular-resizable-element';
|
||||
import { ThemeService } from '../../../services/theme.service';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -49,16 +50,19 @@ export class LogConsoleComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
public styleInside: object = { height: `120px` };
|
||||
|
||||
isDraggingEnabled: boolean = false;
|
||||
public isLightThemeEnabled: boolean = false;
|
||||
|
||||
constructor(
|
||||
private projectWebServiceHandler: ProjectWebServiceHandler,
|
||||
private nodeService: NodeService,
|
||||
private nodesDataSource: NodesDataSource,
|
||||
private logEventsDataSource: LogEventsDataSource,
|
||||
private httpService: HttpServer
|
||||
private httpService: HttpServer,
|
||||
private themeService: ThemeService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.themeService.getActualTheme() === 'light' ? this.isLightThemeEnabled = true : this.isLightThemeEnabled = false;
|
||||
this.nodeSubscription = this.projectWebServiceHandler.nodeNotificationEmitter.subscribe((event) => {
|
||||
let node: Node = event.event as Node;
|
||||
let message: string = '';
|
||||
|
@ -5,8 +5,12 @@
|
||||
width: 40px;
|
||||
margin-right: 12px !important;
|
||||
margin-left: 12px !important;
|
||||
background: #263238;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.marked {
|
||||
color: #0097a7!important;
|
||||
}
|
||||
|
@ -2,9 +2,8 @@
|
||||
matTooltip="Add a note"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
[color]="drawTools.isTextChosen ? 'primary' : 'basic'"
|
||||
(click)="addDrawing('text')">
|
||||
<mat-icon>create</mat-icon>
|
||||
<mat-icon [ngClass]="{unmarkedLight: !drawTools.isTextChosen && isLightThemeEnabled, marked: drawTools.isTextChosen}">create</mat-icon>
|
||||
</button>
|
||||
<input
|
||||
type="file"
|
||||
@ -23,19 +22,17 @@
|
||||
matTooltip="Draw a rectangle"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
[color]="drawTools.isRectangleChosen ? 'primary' : 'basic'"
|
||||
(click)="addDrawing('rectangle')">
|
||||
<mat-icon>crop_3_2</mat-icon>
|
||||
<mat-icon [ngClass]="{unmarkedLight: !drawTools.isRectangleChosen && isLightThemeEnabled, marked: drawTools.isRectangleChosen}">crop_3_2</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Draw an ellipse"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
[color]="drawTools.isEllipseChosen ? 'primary' : 'basic'"
|
||||
(click)="addDrawing('ellipse')">
|
||||
<mat-icon>panorama_fish_eye</mat-icon>
|
||||
<mat-icon [ngClass]="{unmarkedLight: !drawTools.isEllipseChosen && isLightThemeEnabled, marked: drawTools.isEllipseChosen}">panorama_fish_eye</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
<button *ngIf="!isLightThemeEnabled"
|
||||
matTooltip="Draw a line"
|
||||
mat-icon-button class="menu-button"
|
||||
(click)="addDrawing('line')">
|
||||
@ -49,13 +46,26 @@
|
||||
style="stroke:white;stroke-width:2"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button *ngIf="isLightThemeEnabled"
|
||||
matTooltip="Draw a line"
|
||||
mat-icon-button class="menu-button"
|
||||
(click)="addDrawing('line')">
|
||||
<svg height="40" width="40">
|
||||
<line
|
||||
[ngClass]="{ selected: drawTools.isLineChosen }"
|
||||
x1="30"
|
||||
y1="10"
|
||||
x2="10"
|
||||
y2="30"
|
||||
style="stroke:black;stroke-width:2"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Lock or unlock all items"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
[color]="isLocked ? 'primary' : 'basic'"
|
||||
(click)="changeLockValue()">
|
||||
<mat-icon>lock</mat-icon>
|
||||
<mat-icon [ngClass]="{unmarkedLight: !isLocked && isLightThemeEnabled, marked: isLocked}">lock</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Take a screenshot"
|
||||
@ -63,7 +73,7 @@
|
||||
class="menu-button"
|
||||
(click)="takeScreenshot()"
|
||||
>
|
||||
<mat-icon>photo_camera</mat-icon>
|
||||
<mat-icon [ngClass]="{unmarkedLight: !isLocked && isLightThemeEnabled}">photo_camera</mat-icon>
|
||||
</button>
|
||||
<app-drawing-added
|
||||
[server]="server"
|
||||
|
@ -5,7 +5,7 @@
|
||||
width: 40px;
|
||||
margin-right: 12px !important;
|
||||
margin-left: 12px !important;
|
||||
background: #263238;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
@ -22,3 +22,15 @@ mat-divider.divider {
|
||||
.non-visible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.unmarked {
|
||||
color: white!important;
|
||||
}
|
||||
|
||||
.unmarkedLight {
|
||||
color: black!important;
|
||||
}
|
||||
|
||||
.marked {
|
||||
color: #0097a7!important;
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import { ElectronService } from 'ngx-electron';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { ScreenshotDialogComponent, Screenshot } from '../screenshot-dialog/screenshot-dialog.component';
|
||||
import { saveAsPng, saveAsJpeg } from 'save-html-as-image';
|
||||
import { ThemeService } from '../../../services/theme.service';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -31,16 +32,20 @@ export class ProjectMapMenuComponent implements OnInit, OnDestroy {
|
||||
isTextChosen: false
|
||||
};
|
||||
public isLocked: boolean = false;
|
||||
public isLightThemeEnabled: boolean = false;
|
||||
|
||||
constructor(
|
||||
private toolsService: ToolsService,
|
||||
private mapSettingsService: MapSettingsService,
|
||||
private drawingService: DrawingService,
|
||||
private symbolService: SymbolService,
|
||||
private dialog: MatDialog
|
||||
private dialog: MatDialog,
|
||||
private themeService: ThemeService
|
||||
) {}
|
||||
|
||||
ngOnInit() {}
|
||||
ngOnInit() {
|
||||
this.themeService.getActualTheme() === 'light' ? this.isLightThemeEnabled = true : this.isLightThemeEnabled = false;
|
||||
}
|
||||
|
||||
public takeScreenshot() {
|
||||
const dialogRef = this.dialog.open(ScreenshotDialogComponent, {
|
||||
|
@ -33,11 +33,15 @@
|
||||
></app-experimental-map>
|
||||
|
||||
<div class="project-toolbar">
|
||||
<mat-toolbar color="primary" class="project-toolbar">
|
||||
<mat-toolbar-row>
|
||||
<mat-toolbar color="primary" class="project-toolbar" [ngClass]="{lightTheme: isLightThemeEnabled}">
|
||||
<mat-toolbar-row *ngIf="!isLightThemeEnabled">
|
||||
<button matTooltip="Open menu" mat-icon-button [matMenuTriggerFor]="mainMenu"><mat-icon svgIcon="gns3"></mat-icon></button>
|
||||
</mat-toolbar-row>
|
||||
|
||||
<mat-toolbar-row *ngIf="isLightThemeEnabled">
|
||||
<button matTooltip="Open menu" mat-icon-button [matMenuTriggerFor]="mainMenu"><mat-icon svgIcon="gns3black"></mat-icon></button>
|
||||
</mat-toolbar-row>
|
||||
|
||||
<mat-menu #mainMenu="matMenu" [overlapTrigger]="false">
|
||||
<button mat-menu-item [routerLink]="['/server', server.id, 'projects']">
|
||||
<mat-icon>work</mat-icon>
|
||||
@ -145,24 +149,24 @@
|
||||
</mat-toolbar>
|
||||
</div>
|
||||
|
||||
<div id="show-menu-wrapper" [ngClass]="{ shadowed: !isProjectMapMenuVisible }" *ngIf="!readonly">
|
||||
<button class="arrow-button" mat-icon-button (click)="showMenu()"><mat-icon>keyboard_arrow_right</mat-icon></button>
|
||||
<div id="show-menu-wrapper" [ngClass]="{lightTheme: isLightThemeEnabled, shadowed: !isProjectMapMenuVisible }" *ngIf="!readonly">
|
||||
<button [ngClass]="{lightTheme: isLightThemeEnabled}" class="arrow-button" mat-icon-button (click)="showMenu()"><mat-icon class="unmarked">keyboard_arrow_right</mat-icon></button>
|
||||
</div>
|
||||
|
||||
<div id="menu-wrapper" [ngClass]="{ extended: isProjectMapMenuVisible }">
|
||||
<div id="menu-wrapper" [ngClass]="{lightTheme: isLightThemeEnabled, extended: isProjectMapMenuVisible }">
|
||||
<app-nodes-menu [server]="server" [project]="project"></app-nodes-menu>
|
||||
<mat-divider class="divider" [vertical]="true"></mat-divider>
|
||||
<app-project-map-menu [server]="server" [project]="project"></app-project-map-menu>
|
||||
<button class="arrow-button" mat-icon-button (click)="hideMenu()"><mat-icon>keyboard_arrow_left</mat-icon></button>
|
||||
<button [ngClass]="{lightTheme: isLightThemeEnabled}" class="arrow-button" mat-icon-button (click)="hideMenu()"><mat-icon class="unmarked">keyboard_arrow_left</mat-icon></button>
|
||||
</div>
|
||||
|
||||
<app-context-menu [project]="project" [server]="server"></app-context-menu>
|
||||
</div>
|
||||
|
||||
<div id="zoom-buttons">
|
||||
<button class="zoom-button" (click)="zoomIn()"><mat-icon>zoom_in</mat-icon></button>
|
||||
<button class="zoom-button" (click)="resetZoom()"><mat-icon>adjust</mat-icon></button>
|
||||
<button class="zoom-button" (click)="zoomOut()"><mat-icon>zoom_out</mat-icon></button>
|
||||
<div [ngClass]="{lightTheme: isLightThemeEnabled}" class="zoom-buttons">
|
||||
<button [ngClass]="{lightTheme: isLightThemeEnabled}" class="zoom-button" (click)="zoomIn()"><mat-icon>zoom_in</mat-icon></button>
|
||||
<button [ngClass]="{lightTheme: isLightThemeEnabled}" class="zoom-button" (click)="resetZoom()"><mat-icon>adjust</mat-icon></button>
|
||||
<button [ngClass]="{lightTheme: isLightThemeEnabled}" class="zoom-button" (click)="zoomOut()"><mat-icon>zoom_out</mat-icon></button>
|
||||
</div>
|
||||
|
||||
<app-progress></app-progress>
|
||||
|
@ -21,6 +21,16 @@ g.node:hover {
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
}
|
||||
|
||||
img {
|
||||
-webkit-filter: invert(1);
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
.lightTheme {
|
||||
background: white!important;
|
||||
color: black!important;
|
||||
}
|
||||
|
||||
#show-menu-wrapper {
|
||||
position: fixed;
|
||||
background: transparent;
|
||||
@ -66,7 +76,7 @@ g.node:hover {
|
||||
width: 40px;
|
||||
margin-right: 12px !important;
|
||||
margin-left: 12px !important;
|
||||
background: #263238;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
@ -93,18 +103,19 @@ mat-divider.divider {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
#zoom-buttons {
|
||||
.zoom-buttons {
|
||||
position: fixed;
|
||||
background: #263238;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
display: grid;
|
||||
color: white;
|
||||
|
||||
.zoom-button {
|
||||
outline: none;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
background: #263238;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 1.25rem;
|
||||
@ -114,21 +125,6 @@ mat-divider.divider {
|
||||
margin-left: -6px;
|
||||
}
|
||||
}
|
||||
|
||||
.zoom-button-white {
|
||||
outline: none;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
color: #263238;
|
||||
border: none;
|
||||
background: white;
|
||||
font-size: 1.25rem;
|
||||
font-weight: bold;
|
||||
|
||||
mat-icon {
|
||||
margin-left: -6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@-moz-document url-prefix() {
|
||||
|
@ -65,6 +65,7 @@ import { NavigationDialogComponent } from '../projects/navigation-dialog/navigat
|
||||
import { ConfirmationBottomSheetComponent } from '../projects/confirmation-bottomsheet/confirmation-bottomsheet.component';
|
||||
import { NodeAddedEvent } from '../template/template-list-dialog/template-list-dialog.component';
|
||||
import { NotificationService } from '../../services/notification.service';
|
||||
import { ThemeService } from '../../services/theme.service';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -102,6 +103,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
private scrollX: number = 0;
|
||||
private scrollY: number = 0;
|
||||
private scrollEnabled: boolean = false;
|
||||
public isLightThemeEnabled: boolean = false;
|
||||
|
||||
@ViewChild(ContextMenuComponent, {static: false}) contextMenu: ContextMenuComponent;
|
||||
@ViewChild(D3MapComponent, {static: false}) mapChild: D3MapComponent;
|
||||
@ -150,10 +152,12 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
private ethernetLinkWidget: EthernetLinkWidget,
|
||||
private serialLinkWidget: SerialLinkWidget,
|
||||
private bottomSheet: MatBottomSheet,
|
||||
private notificationService: NotificationService
|
||||
private notificationService: NotificationService,
|
||||
private themeService: ThemeService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.themeService.getActualTheme() === 'light' ? this.isLightThemeEnabled = true : this.isLightThemeEnabled = false;
|
||||
this.settings = this.settingsService.getAll();
|
||||
this.isTopologySummaryVisible = this.mapSettingsService.isTopologySummaryVisible;
|
||||
this.isConsoleVisible = this.mapSettingsService.isLogConsoleVisible;
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="dialogWrapper">
|
||||
<div class="dialogWrapper" [ngClass]="{lightTheme: isLightThemeEnabled}">
|
||||
<div class="title">{{message}}</div>
|
||||
<div>
|
||||
<button mat-button (click)="onNoClick()">No</button>
|
||||
|
@ -7,8 +7,8 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
mat-bottom-sheet-container {
|
||||
background: #263238;
|
||||
.lightTheme {
|
||||
background-color: white!important;
|
||||
}
|
||||
|
||||
.title {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Component, OnInit, Inject } from '@angular/core';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA, MatBottomSheetRef } from '@angular/material';
|
||||
import { ThemeService } from '../../../services/theme.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-confirmation-bottomsheet',
|
||||
@ -8,10 +9,16 @@ import { MatDialogRef, MAT_DIALOG_DATA, MatBottomSheetRef } from '@angular/mater
|
||||
})
|
||||
export class ConfirmationBottomSheetComponent implements OnInit {
|
||||
message: string = '';
|
||||
isLightThemeEnabled: boolean = false;
|
||||
|
||||
constructor(private bottomSheetRef: MatBottomSheetRef<ConfirmationBottomSheetComponent>) {}
|
||||
constructor(
|
||||
private bottomSheetRef: MatBottomSheetRef<ConfirmationBottomSheetComponent>,
|
||||
private themeService: ThemeService
|
||||
) {}
|
||||
|
||||
ngOnInit() {}
|
||||
ngOnInit() {
|
||||
this.themeService.getActualTheme() === 'light' ? this.isLightThemeEnabled = true : this.isLightThemeEnabled = false;
|
||||
}
|
||||
|
||||
onNoClick(): void {
|
||||
this.bottomSheetRef.dismiss(false);
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="dialogWrapper">
|
||||
<div class="dialogWrapper" [ngClass]="{lightTheme: isLightThemeEnabled}">
|
||||
<div class="title"> Do you want to navigate to {{projectMessage}}?</div>
|
||||
<div>
|
||||
<button mat-button (click)="onNoClick()">No</button>
|
||||
|
@ -7,8 +7,8 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
mat-bottom-sheet-container {
|
||||
background: #263238;
|
||||
.lightTheme {
|
||||
background-color: white!important;
|
||||
}
|
||||
|
||||
.title {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Component, OnInit, Inject } from '@angular/core';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA, MatBottomSheetRef } from '@angular/material';
|
||||
import { ThemeService } from '../../../services/theme.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-navigation-dialog',
|
||||
@ -8,10 +9,16 @@ import { MatDialogRef, MAT_DIALOG_DATA, MatBottomSheetRef } from '@angular/mater
|
||||
})
|
||||
export class NavigationDialogComponent implements OnInit {
|
||||
projectMessage: string = '';
|
||||
isLightThemeEnabled: boolean = false;
|
||||
|
||||
constructor(private bottomSheetRef: MatBottomSheetRef<NavigationDialogComponent>) {}
|
||||
constructor(
|
||||
private bottomSheetRef: MatBottomSheetRef<NavigationDialogComponent>,
|
||||
private themeService: ThemeService
|
||||
) {}
|
||||
|
||||
ngOnInit() {}
|
||||
ngOnInit() {
|
||||
this.themeService.getActualTheme() === 'light' ? this.isLightThemeEnabled = true : this.isLightThemeEnabled = false;
|
||||
}
|
||||
|
||||
onNoClick(): void {
|
||||
this.bottomSheetRef.dismiss(false);
|
||||
|
@ -42,6 +42,19 @@
|
||||
</div>
|
||||
|
||||
</mat-expansion-panel>
|
||||
|
||||
<mat-expansion-panel [expanded]="false">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title> Theme settings </mat-panel-title>
|
||||
<mat-panel-description> Customize theme settings </mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<div class="theme-panel">
|
||||
<button mat-raised-button (click)="setDarkMode(false)">Switch to light theme</button>
|
||||
<button mat-raised-button (click)="setDarkMode(true)">Switch to dark theme</button>
|
||||
</div>
|
||||
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
</div>
|
||||
|
||||
|
@ -0,0 +1,5 @@
|
||||
.theme-panel {
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
}
|
@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core';
|
||||
import { SettingsService } from '../../services/settings.service';
|
||||
import { ToasterService } from '../../services/toaster.service';
|
||||
import { ConsoleService } from '../../services/settings/console.service';
|
||||
import { ThemeService } from '../../services/theme.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings',
|
||||
@ -15,7 +16,9 @@ export class SettingsComponent implements OnInit {
|
||||
constructor(
|
||||
private settingsService: SettingsService,
|
||||
private toaster: ToasterService,
|
||||
private consoleService: ConsoleService) {}
|
||||
private consoleService: ConsoleService,
|
||||
private themeService: ThemeService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.settings = this.settingsService.getAll();
|
||||
@ -26,4 +29,8 @@ export class SettingsComponent implements OnInit {
|
||||
this.settingsService.setAll(this.settings);
|
||||
this.toaster.success('Settings have been saved.');
|
||||
}
|
||||
|
||||
setDarkMode(value: boolean) {
|
||||
this.themeService.setDarkMode(value);
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
(mousedown)="toggleDragging(true)"
|
||||
*ngIf="projectsStatistics"
|
||||
[ngStyle]="style"
|
||||
[ngClass]="{lightTheme: isLightThemeEnabled}"
|
||||
mwlResizable
|
||||
[validateResize]="validate"
|
||||
[resizeEdges]="{ right: true, left: true, bottom: true, top: true }"
|
||||
|
@ -11,6 +11,11 @@
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.lightTheme {
|
||||
background: white!important;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.summaryHeaderMenu {
|
||||
height: 24px;
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import { Compute } from '../../models/compute';
|
||||
import { ComputeService } from '../../services/compute.service';
|
||||
import { LinksDataSource } from '../../cartography/datasources/links-datasource';
|
||||
import { ResizeEvent } from 'angular-resizable-element';
|
||||
import { ThemeService } from '../../services/theme.service';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -36,18 +37,21 @@ export class TopologySummaryComponent implements OnInit, OnDestroy {
|
||||
captureFilterEnabled: boolean = false;
|
||||
packetFilterEnabled: boolean = false;
|
||||
computes: Compute[] = [];
|
||||
|
||||
isTopologyVisible: boolean = true;
|
||||
|
||||
isDraggingEnabled: boolean = false;
|
||||
isLightThemeEnabled: boolean = false;
|
||||
|
||||
constructor(
|
||||
private nodesDataSource: NodesDataSource,
|
||||
private projectService: ProjectService,
|
||||
private computeService: ComputeService,
|
||||
private linksDataSource: LinksDataSource
|
||||
private linksDataSource: LinksDataSource,
|
||||
private themeService: ThemeService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.themeService.getActualTheme() === 'light' ? this.isLightThemeEnabled = true : this.isLightThemeEnabled = false;
|
||||
this.subscriptions.push(
|
||||
this.nodesDataSource.changes.subscribe((nodes: Node[]) => {
|
||||
this.nodes = nodes;
|
||||
|
@ -21,7 +21,6 @@ app-default-layout {
|
||||
padding: 20px;
|
||||
margin: auto 0 0 0;
|
||||
/*background-color: #0097a7;*/
|
||||
color: white;
|
||||
}
|
||||
|
||||
.default-content {
|
||||
@ -45,7 +44,6 @@ header {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
padding: 28px 8px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.default-header {
|
||||
|
67
src/app/services/theme.service.ts
Normal file
67
src/app/services/theme.service.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import { Injectable, RendererFactory2, Renderer2, Inject, EventEmitter } from '@angular/core';
|
||||
import { Observable, BehaviorSubject, combineLatest } from 'rxjs';
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ThemeService {
|
||||
private _mainTheme$: BehaviorSubject<string> = new BehaviorSubject('theme-default');
|
||||
private _darkMode$: BehaviorSubject<boolean> = new BehaviorSubject(false);
|
||||
private _renderer: Renderer2;
|
||||
private head: HTMLElement;
|
||||
private themeLinks: HTMLElement[] = [];
|
||||
darkMode$: Observable<boolean> = this._darkMode$.asObservable();
|
||||
theme$: Observable<[string, boolean]>;
|
||||
|
||||
public themeChanged = new EventEmitter<string>();
|
||||
public savedTheme: string = 'dark';
|
||||
|
||||
constructor(
|
||||
rendererFactory: RendererFactory2,
|
||||
@Inject(DOCUMENT) document: Document
|
||||
) {
|
||||
this.head = document.head;
|
||||
this._renderer = rendererFactory.createRenderer(null, null);
|
||||
this.theme$ = combineLatest(this._mainTheme$, this._darkMode$);
|
||||
this.theme$.subscribe(async ([mainTheme, darkMode]) => {
|
||||
const cssExt = '.css';
|
||||
const cssFilename = darkMode ? mainTheme + '-dark' + cssExt : mainTheme + cssExt;
|
||||
await this.loadCss(cssFilename);
|
||||
if (this.themeLinks.length == 2)
|
||||
this._renderer.removeChild(this.head, this.themeLinks.shift());
|
||||
})
|
||||
}
|
||||
|
||||
getActualTheme() {
|
||||
return this.savedTheme;
|
||||
}
|
||||
|
||||
setMainTheme(name: string) {
|
||||
this._mainTheme$.next(name);
|
||||
}
|
||||
|
||||
setDarkMode(value: boolean) {
|
||||
this._darkMode$.next(value);
|
||||
localStorage.removeItem('theme');
|
||||
if (value) {
|
||||
this.savedTheme = 'dark';
|
||||
this.themeChanged.emit(this.savedTheme);
|
||||
localStorage.setItem('theme', 'dark');
|
||||
} else {
|
||||
this.savedTheme = 'light';
|
||||
this.themeChanged.emit(this.savedTheme);
|
||||
localStorage.setItem('theme', 'light');
|
||||
}
|
||||
}
|
||||
|
||||
private async loadCss(filename: string) {
|
||||
return new Promise(resolve => {
|
||||
const linkEl: HTMLElement = this._renderer.createElement('link');
|
||||
this._renderer.setAttribute(linkEl, 'rel', 'stylesheet');
|
||||
this._renderer.setAttribute(linkEl, 'type', 'text/css');
|
||||
this._renderer.setAttribute(linkEl, 'href', filename);
|
||||
this._renderer.setProperty(linkEl, 'onload', resolve);
|
||||
this._renderer.appendChild(this.head, linkEl);
|
||||
this.themeLinks = [...this.themeLinks, linkEl];
|
||||
})
|
||||
}
|
||||
}
|
88
src/assets/gns3_icon_black.svg
Normal file
88
src/assets/gns3_icon_black.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 11 KiB |
70
src/theme-light.scss
Normal file
70
src/theme-light.scss
Normal file
@ -0,0 +1,70 @@
|
||||
@import '~@angular/material/theming';
|
||||
@import '~material-design-icons/iconfont/material-icons.css';
|
||||
@import '~typeface-roboto/index.css';
|
||||
|
||||
// Include non-theme styles for core.
|
||||
@include mat-core();
|
||||
|
||||
// Define a theme.
|
||||
$primary: mat-palette($mat-cyan, 700, 500, 900);
|
||||
$accent: mat-palette($mat-blue, A200, A100, A700);
|
||||
|
||||
$theme: mat-light-theme($primary, $accent);
|
||||
|
||||
$light-palette: mat-palette($mat-blue, 900, A100, A400);
|
||||
$light-background: mat-light-theme($light-palette, $primary);
|
||||
|
||||
$mat-light-theme-background: (
|
||||
status-bar: black,
|
||||
app-bar: map_get($mat-blue, 900),
|
||||
background: white,
|
||||
hover: rgba(white, 0.04),
|
||||
card: white,
|
||||
dialog: white,
|
||||
disabled-button: $white-12-opacity,
|
||||
raised-button: white,
|
||||
focused-button: $white-6-opacity,
|
||||
selected-button: map_get($mat-blue, 900),
|
||||
selected-disabled-button: map_get($mat-blue, 800),
|
||||
disabled-button-toggle: black,
|
||||
unselected-chip: map_get($mat-blue, 700),
|
||||
disabled-list-option: black
|
||||
);
|
||||
|
||||
$theme: map-merge(
|
||||
$theme,
|
||||
(
|
||||
background: $mat-light-theme-background
|
||||
)
|
||||
);
|
||||
|
||||
@include mat-core-theme($theme);
|
||||
@include mat-autocomplete-theme($theme);
|
||||
@include mat-button-theme($theme);
|
||||
@include mat-button-toggle-theme($theme);
|
||||
@include mat-card-theme($theme);
|
||||
@include mat-checkbox-theme($theme);
|
||||
@include mat-chips-theme($theme);
|
||||
@include mat-table-theme($theme);
|
||||
@include mat-datepicker-theme($theme);
|
||||
@include mat-dialog-theme($theme);
|
||||
@include mat-expansion-panel-theme($theme);
|
||||
@include mat-form-field-theme($theme);
|
||||
@include mat-grid-list-theme($theme);
|
||||
@include mat-icon-theme($theme);
|
||||
@include mat-input-theme($theme);
|
||||
@include mat-list-theme($theme);
|
||||
@include mat-menu-theme($theme);
|
||||
@include mat-paginator-theme($theme);
|
||||
@include mat-progress-bar-theme($theme);
|
||||
@include mat-progress-spinner-theme($theme);
|
||||
@include mat-radio-theme($theme);
|
||||
@include mat-select-theme($theme);
|
||||
@include mat-sidenav-theme($theme);
|
||||
@include mat-slide-toggle-theme($theme);
|
||||
@include mat-slider-theme($theme);
|
||||
@include mat-stepper-theme($theme);
|
||||
@include mat-tabs-theme($theme);
|
||||
@include mat-toolbar-theme($light-background);
|
||||
@include mat-tooltip-theme($theme);
|
||||
@include mat-snack-bar-theme($theme);
|
Loading…
x
Reference in New Issue
Block a user