mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-05-08 11:38:38 +00:00
Material design over ng-bootstrap
This commit is contained in:
parent
805496c714
commit
6bfb4b5fed
22
package-lock.json
generated
22
package-lock.json
generated
@ -46,9 +46,17 @@
|
||||
}
|
||||
},
|
||||
"@angular/animations": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-4.3.3.tgz",
|
||||
"integrity": "sha1-tx3dRTZzkp9VCxccypmVKzqqgxw=",
|
||||
"version": "4.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-4.4.6.tgz",
|
||||
"integrity": "sha1-+mYYmaik44y3xYPHpcl85l1ZKjU=",
|
||||
"requires": {
|
||||
"tslib": "1.7.1"
|
||||
}
|
||||
},
|
||||
"@angular/cdk": {
|
||||
"version": "2.0.0-beta.12",
|
||||
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-2.0.0-beta.12.tgz",
|
||||
"integrity": "sha1-OiQ8tiuT9OA5EgunD5ANyeI1Yi4=",
|
||||
"requires": {
|
||||
"tslib": "1.7.1"
|
||||
}
|
||||
@ -180,6 +188,14 @@
|
||||
"integrity": "sha1-Y4z8oTQsOU9xP4wPo5jCYDLhVvQ=",
|
||||
"dev": true
|
||||
},
|
||||
"@angular/material": {
|
||||
"version": "2.0.0-beta.12",
|
||||
"resolved": "https://registry.npmjs.org/@angular/material/-/material-2.0.0-beta.12.tgz",
|
||||
"integrity": "sha1-cbbQt7AhiR5dDjaIwdS9eMdFf1g=",
|
||||
"requires": {
|
||||
"tslib": "1.7.1"
|
||||
}
|
||||
},
|
||||
"@angular/platform-browser": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-4.3.3.tgz",
|
||||
|
@ -12,12 +12,14 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^4.0.0",
|
||||
"@angular/animations": "^4.4.6",
|
||||
"@angular/cdk": "^2.0.0-beta.12",
|
||||
"@angular/common": "^4.0.0",
|
||||
"@angular/compiler": "^4.0.0",
|
||||
"@angular/core": "^4.0.0",
|
||||
"@angular/forms": "^4.0.0",
|
||||
"@angular/http": "^4.0.0",
|
||||
"@angular/material": "^2.0.0-beta.12",
|
||||
"@angular/platform-browser": "^4.0.0",
|
||||
"@angular/platform-browser-dynamic": "^4.0.0",
|
||||
"@angular/router": "^4.0.0",
|
||||
|
@ -1,4 +1,7 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import {Http} from "@angular/http";
|
||||
import {MatIconRegistry} from "@angular/material";
|
||||
import {DomSanitizer} from "@angular/platform-browser";
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@ -6,6 +9,10 @@ import { Component, OnInit } from '@angular/core';
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
constructor(http: Http, iconReg: MatIconRegistry, sanitizer: DomSanitizer) {
|
||||
iconReg.addSvgIcon('gns3', sanitizer.bypassSecurityTrustResourceUrl('./assets/gns3_icon.svg'));
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { MapComponent } from './map/map.component';
|
||||
import { ProjectMapComponent } from './project-map/project-map.component';
|
||||
import { ServerCreateModalComponent, ServersComponent } from './servers/servers.component';
|
||||
import { ServersComponent, AddServerDialogComponent } from './servers/servers.component';
|
||||
import { ProjectsComponent } from './projects/projects.component';
|
||||
|
||||
import { VersionService } from './services/version.service';
|
||||
@ -23,13 +23,28 @@ import { HttpServer } from "./services/http-server.service";
|
||||
import { DefaultLayoutComponent } from './default-layout/default-layout.component';
|
||||
|
||||
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import {
|
||||
MatButtonModule,
|
||||
MatCardModule,
|
||||
MatMenuModule,
|
||||
MatToolbarModule,
|
||||
MatIconModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatTableModule,
|
||||
MatDialogModule
|
||||
} from '@angular/material';
|
||||
|
||||
import {CdkTableModule} from "@angular/cdk/table";
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
MapComponent,
|
||||
ProjectMapComponent,
|
||||
ServersComponent,
|
||||
ServerCreateModalComponent,
|
||||
AddServerDialogComponent,
|
||||
ProjectsComponent,
|
||||
DefaultLayoutComponent,
|
||||
],
|
||||
@ -38,7 +53,18 @@ import { DefaultLayoutComponent } from './default-layout/default-layout.componen
|
||||
BrowserModule,
|
||||
HttpModule,
|
||||
AppRoutingModule,
|
||||
FormsModule
|
||||
FormsModule,
|
||||
BrowserAnimationsModule,
|
||||
MatButtonModule,
|
||||
MatMenuModule,
|
||||
MatCardModule,
|
||||
MatToolbarModule,
|
||||
MatIconModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatTableModule,
|
||||
MatDialogModule,
|
||||
CdkTableModule
|
||||
],
|
||||
providers: [
|
||||
D3Service,
|
||||
@ -50,7 +76,7 @@ import { DefaultLayoutComponent } from './default-layout/default-layout.componen
|
||||
HttpServer,
|
||||
],
|
||||
entryComponents: [
|
||||
ServerCreateModalComponent,
|
||||
AddServerDialogComponent
|
||||
],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
|
@ -0,0 +1,53 @@
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
app-root, app-default-layout {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
app-default-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
flex-shrink: 0;
|
||||
padding: 20px;
|
||||
background-color: #e91e63;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.default-content {
|
||||
margin: 0 auto;
|
||||
max-width: 940px;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
main {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.default-header h1 {
|
||||
font-weight: 300;
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
padding: 28px 8px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.default-header {
|
||||
padding-left: 20px;
|
||||
background-color: #e91e63;
|
||||
}
|
||||
|
||||
.buttons-bar {
|
||||
padding-top: 10px;
|
||||
text-align: right;
|
||||
}
|
@ -1,19 +1,18 @@
|
||||
<nav class="navbar navbar-expand-lg navbar-dark">
|
||||
<a class="navbar-brand" href="/"><img src="assets/logo-header.png"></a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" [routerLink]="['/servers']" >Servers <span class="sr-only">(current)</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<header>
|
||||
<mat-toolbar color="primary">
|
||||
<button mat-icon-button>
|
||||
<mat-icon svgIcon="gns3"></mat-icon>
|
||||
</button>
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
<a mat-button routerLink="/servers" >Servers</a>
|
||||
</mat-toolbar>
|
||||
</header>
|
||||
|
||||
<footer class="footer">
|
||||
<span class="text-muted">GNS3 Web UI demo</span>
|
||||
<main class="mat-app-background">
|
||||
<router-outlet></router-outlet>
|
||||
</main>
|
||||
|
||||
<footer class="footer primary-light-bg">
|
||||
GNS3 Web UI demo © 2017
|
||||
</footer>
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
html {
|
||||
position: static;
|
||||
height: 100%;
|
||||
}
|
||||
/*html {*/
|
||||
/*position: static;*/
|
||||
/*height: 100%;*/
|
||||
/*}*/
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
/*body {*/
|
||||
/*height: 100%;*/
|
||||
/*margin: 0;*/
|
||||
/*margin-bottom: 0 !important;*/
|
||||
/*}*/
|
||||
|
||||
app-root, app-project-map, .project-map, app-map, svg {
|
||||
height: 100%;
|
||||
}
|
||||
/*app-root, app-project-map, .project-map, app-map, svg {*/
|
||||
/*height: 100%;*/
|
||||
/*}*/
|
||||
|
||||
|
16
src/app/servers/add-server-dialog.html
Normal file
16
src/app/servers/add-server-dialog.html
Normal file
@ -0,0 +1,16 @@
|
||||
<h1 mat-dialog-title>Add server</h1>
|
||||
<div mat-dialog-content>
|
||||
<mat-form-field>
|
||||
<input matInput tabindex="1" [(ngModel)]="server.name" placeholder="Name">
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<input matInput tabindex="1" [(ngModel)]="server.ip" placeholder="IP">
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<input matInput tabindex="1" [(ngModel)]="server.port" placeholder="Port">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div mat-dialog-actions>
|
||||
<button mat-button (click)="onAddClick()" tabindex="2">Add</button>
|
||||
<button mat-button (click)="onNoClick()" tabindex="-1">No Thanks</button>
|
||||
</div>
|
@ -1,27 +0,0 @@
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Add server</h4>
|
||||
<button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form #f="ngForm">
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
<input type="text" class="form-control" id="name" name="name" placeholder="Enter name" [(ngModel)]="server.name" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="ip">IP</label>
|
||||
<input type="text" class="form-control" id="ip" name="ip" placeholder="Enter IP" [(ngModel)]="server.ip" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="port">Port</label>
|
||||
<input type="number" class="form-control" id="port" name="port" placeholder="Enter Port" [(ngModel)]="server.port" required>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-dark" (click)="activeModal.dismiss()">Close</button>
|
||||
|
||||
<button type="button" class="btn btn-success" (click)="add()" [disabled]="!f.valid">Add</button>
|
||||
</div>
|
@ -1,29 +1,49 @@
|
||||
<div class="container page">
|
||||
<h1>Servers</h1>
|
||||
|
||||
<div class="row">
|
||||
<table class="table table-inverse">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Name</th>
|
||||
<th>IP:Port</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let server of servers">
|
||||
<th scope="row">{{ server.id }}</th>
|
||||
<td><a [routerLink]="['/server', server.id, 'projects']">{{ server.name }}</a></td>
|
||||
<td>{{ server.ip }}:{{ server.port }}</td>
|
||||
<td><button class="btn btn-outline-danger btn-sm " (click)="deleteServer(server)">Remove</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="content">
|
||||
<div class="default-header">
|
||||
<h1>Servers</h1>
|
||||
</div>
|
||||
<div class="default-content">
|
||||
|
||||
<div class="row">
|
||||
<button class="btn btn-primary btn-lg active" (click)="createModal()">Add server</button>
|
||||
<div class="example-container mat-elevation-z8">
|
||||
<mat-table #table [dataSource]="dataSource">
|
||||
|
||||
<ng-container matColumnDef="id">
|
||||
<mat-header-cell *matHeaderCellDef> ID </mat-header-cell>
|
||||
<mat-cell *matCellDef="let row"> {{row.id}} </mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="name">
|
||||
<mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
|
||||
<mat-cell *matCellDef="let row"> {{row.name}} </mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="ip">
|
||||
<mat-header-cell *matHeaderCellDef> IP </mat-header-cell>
|
||||
<mat-cell *matCellDef="let row"> {{row.ip}} </mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="port">
|
||||
<mat-header-cell *matHeaderCellDef> Port </mat-header-cell>
|
||||
<mat-cell *matCellDef="let row"> {{row.port}} </mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<mat-header-cell *matHeaderCellDef> Actions </mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">
|
||||
<button mat-icon-button (click)="deleteServer(row)">
|
||||
<mat-icon aria-label="Remove server">delete</mat-icon>
|
||||
</button>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
>
|
||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
|
||||
</mat-table>
|
||||
</div>
|
||||
|
||||
<div class="buttons-bar">
|
||||
<button mat-raised-button color="primary" (click)="createModal()">Add server</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -1,23 +1,18 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { NgbModal, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
|
||||
import { Server } from "../models/server";
|
||||
import { ServerService } from "../services/server.service";
|
||||
import {DataSource} from "@angular/cdk/collections";
|
||||
import {Observable} from "rxjs/Observable";
|
||||
import {MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material';
|
||||
import {BehaviorSubject} from "rxjs/BehaviorSubject";
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-server-create-modal',
|
||||
templateUrl: './server-create-modal.component.html'
|
||||
})
|
||||
export class ServerCreateModalComponent {
|
||||
public server = new Server();
|
||||
|
||||
constructor(public activeModal: NgbActiveModal) {}
|
||||
|
||||
add() {
|
||||
this.activeModal.close(this.server);
|
||||
}
|
||||
}
|
||||
import 'rxjs/add/operator/startWith';
|
||||
import 'rxjs/add/observable/merge';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/debounceTime';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
import 'rxjs/add/observable/fromEvent';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -27,32 +22,101 @@ export class ServerCreateModalComponent {
|
||||
})
|
||||
export class ServersComponent implements OnInit {
|
||||
servers: Server[] = [];
|
||||
serverDatabase = new ServerDatabase();
|
||||
dataSource: ServerDataSource;
|
||||
displayedColumns = ['id', 'name', 'ip', 'port', 'actions'];
|
||||
|
||||
constructor(private modalService: NgbModal, private serverService: ServerService) { }
|
||||
constructor(private dialog: MatDialog, private serverService: ServerService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.loadServers();
|
||||
}
|
||||
|
||||
loadServers() {
|
||||
this.serverService.findAll().then((servers: Server[]) => {
|
||||
this.servers = servers;
|
||||
this.serverDatabase.addServers(servers);
|
||||
});
|
||||
|
||||
this.dataSource = new ServerDataSource(this.serverDatabase);
|
||||
}
|
||||
|
||||
createModal() {
|
||||
this.modalService.open(ServerCreateModalComponent).result.then((server: Server) => {
|
||||
this.serverService.create(server).then((created: Server) => {
|
||||
this.loadServers();
|
||||
});
|
||||
}, (rejection) => {
|
||||
const dialogRef = this.dialog.open(AddServerDialogComponent, {
|
||||
width: '250px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(server => {
|
||||
if (server) {
|
||||
this.serverService.create(server).then((created: Server) => {
|
||||
this.serverDatabase.addServer(created);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deleteServer(server: Server) {
|
||||
this.serverService.delete(server).then(() => {
|
||||
this.loadServers();
|
||||
this.serverDatabase.remove(server);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-server-dialog',
|
||||
templateUrl: 'add-server-dialog.html',
|
||||
})
|
||||
export class AddServerDialogComponent {
|
||||
server: Server = new Server();
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<AddServerDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any, serverService: ServerService) {
|
||||
}
|
||||
|
||||
onAddClick(): void {
|
||||
this.dialogRef.close(this.server);
|
||||
}
|
||||
|
||||
onNoClick(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class ServerDatabase {
|
||||
dataChange: BehaviorSubject<Server[]> = new BehaviorSubject<Server[]>([]);
|
||||
|
||||
get data(): Server[] {
|
||||
return this.dataChange.value;
|
||||
}
|
||||
|
||||
public addServer(server: Server) {
|
||||
const servers = this.data.slice();
|
||||
servers.push(server);
|
||||
this.dataChange.next(servers);
|
||||
}
|
||||
|
||||
public addServers(servers: Server[]) {
|
||||
this.dataChange.next(servers);
|
||||
}
|
||||
|
||||
public remove(server: Server) {
|
||||
const index = this.data.indexOf(server);
|
||||
if (index >= 0) {
|
||||
this.data.splice(index, 1);
|
||||
this.dataChange.next(this.data.slice());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerDataSource extends DataSource<any> {
|
||||
constructor(private serverDatabase: ServerDatabase) {
|
||||
super();
|
||||
}
|
||||
|
||||
connect(): Observable<Server[]> {
|
||||
return Observable.merge(this.serverDatabase.dataChange).map(() => {
|
||||
return this.serverDatabase.data;
|
||||
});
|
||||
}
|
||||
|
||||
disconnect() {}
|
||||
|
||||
}
|
||||
|
@ -22,8 +22,15 @@ export class ServerService {
|
||||
}
|
||||
|
||||
public create(server: Server) {
|
||||
return this.onReady(() =>
|
||||
this.indexedDbService.get().add(this.tablename, server));
|
||||
return this.onReady(() => {
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
this.indexedDbService.get().add(this.tablename, server).then((added) => {
|
||||
server.id = added.key;
|
||||
resolve(server);
|
||||
}, reject);
|
||||
});
|
||||
return promise;
|
||||
});
|
||||
}
|
||||
|
||||
public findAll() {
|
||||
|
88
src/assets/gns3_icon.svg
Normal file
88
src/assets/gns3_icon.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 11 KiB |
@ -1,63 +1,7 @@
|
||||
html {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
}
|
||||
/* @TODO: make icons self hosted */
|
||||
@import '~https://fonts.googleapis.com/icon?family=Material+Icons';
|
||||
@import '~@angular/material/prebuilt-themes/pink-bluegrey.css';
|
||||
|
||||
body {
|
||||
margin-bottom: 60px;
|
||||
background-color: #26353f;
|
||||
font-family: Roboto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
}
|
||||
|
||||
.navbar-brand > img {
|
||||
img.logo-header {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.page h1 {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.table-inverse {
|
||||
background-color: #26353f;
|
||||
}
|
||||
|
||||
.table-inverse {
|
||||
background-color: #26353f;
|
||||
}
|
||||
|
||||
.table-inverse thead th {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.table thead th {
|
||||
border-bottom: 2px solid white;
|
||||
}
|
||||
|
||||
.table td, .table th {
|
||||
border-top: 1px solid white;
|
||||
}
|
||||
|
||||
.project-map {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.project-map svg {
|
||||
background-color: lightgray;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user