From a429750ee8bae20547e9d5a7c95b660f0ac9a28f Mon Sep 17 00:00:00 2001 From: "mathias.chouet" <mathias.chouet@irstea.fr> Date: Wed, 23 Jan 2019 18:00:04 +0100 Subject: [PATCH] =?UTF-8?q?D=C3=A9but=20du=20passage=20=C3=A0=20angular-ma?= =?UTF-8?q?terial?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nouvelle barre de navigation nouveau menu latéral --- src/app/app.component.html | 122 +++++++-------- src/app/app.component.scss | 146 ++++++++++++------ src/app/app.component.ts | 90 ++++++++--- src/app/app.module.ts | 11 +- .../calculator-list.component.css | 4 + .../formulaire/definition/form-definition.ts | 3 +- src/app/formulaire/formulaire-element.ts | 2 +- src/index.html | 7 +- src/main.ts | 1 + src/styles.scss | 3 + 10 files changed, 248 insertions(+), 141 deletions(-) diff --git a/src/app/app.component.html b/src/app/app.component.html index b1e921649..b1dd20bba 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,85 +1,75 @@ +<app-layout> + <header> - <!--Navbar --> - <nav class="navbar navbar-expand-sm navbar-dark indigo"> - <!-- Ouverture du sidenav --> - <span id="open-menu" style="font-size:30px;cursor:pointer;color:white" (click)="openNav()">☰</span> - <!-- lien vide pour que le toggler des liens apparaisse à droite --> + <mat-toolbar #navbar id="main-toolbar" color="primary"> + <span class="example-spacer"></span> + + <span id="open-menu" style="font-size:30px;cursor:pointer;color:white" (click)="sidenav.toggle()"> + <mat-icon>menu</mat-icon> + </span> + <a class="navbar-brand"></a> - <!-- toggler des liens --> - <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" - aria-expanded="false" aria-label="Toggle navigation"> - <!-- <span class="navbar-toggler-icon"> </span> --> - <i class="fa fa-ellipsis-v" aria-hidden="true"></i> - </button> + <div [hidden]="tabsFitInNavbar"> + ICI LE SUPER SELECT DE LA MORT + </div> + <div id="tabs-container" [hidden]="! tabsFitInNavbar"> + <button mat-raised-button color="primary" *ngFor="let c of calculators" class="calculator-button" + [routerLink]="['/calculator/',c.uid]" [color]="c.active ? '' : 'primary'" [class.active]="c.active" + (click)="setActiveCalc(c.uid)" [title]="'uid: ' + c.uid"> - <!-- Collapsible content --> - <div class="collapse navbar-collapse" id="navbarSupportedContent"> - <ul id="navbar" class="navbar-nav mr-auto"> - <li class="nav-item calculator-tab" *ngFor="let c of calculators"> - <a class="nav-link waves-light {{getHighlightClass(c.uid)}}" mdbRippleRadius [routerLink]="['/calculator/',c.uid]">{{ c.title }}</a> - </li> - <li class="nav-item" id="add-calculator-tab"> - <i id="new-calculator" class="fa fa-plus-square fa-2x fa-inverse" style='vertical-align: middle' routerLink="/list" (click)='closeNav()'></i> - </li> - </ul> + <span class="calc-name"> + {{ c.title }} + </span> + <span class="calc-type" [innerHTML]="'( ' + c.type + ' )'"></span> + </button> + <!-- <i id="new-calculator" class="fa fa-plus-square fa-2x" style='vertical-align: middle' routerLink="/list" (click)='sidenav.close()'></i> --> </div> - </nav> - <!--/Navbar --> + <button mat-icon-button id="new-calculator" routerLink="/list" (click)="sidenav.close()"> + <mat-icon>add_box</mat-icon> + </button> + </mat-toolbar> + </header> <main> - <!-- sidenav --> - <div class="container"> - <div class="row"> - <div id="mySidenav" class="sidenav"> - <!-- ATTENTION ! pas de href="#" sous peine de rechargement de la page et réinitialisation de l'appli --> - <a id="close-side-nav" class="closebtn" (click)="closeNav()">×</a> - <a id="side-nav-list" routerLink="/list" (click)="closeNav()">{{ uitextSidenavNewCalc }}</a> + + <mat-sidenav-container fxFlexFill class="example-container"> + + <mat-sidenav class="sidenav" #sidenav fxLayout="column" > + <div fxLayout="column"> + <a id="close-side-nav" class="closebtn" (click)="sidenav.close()">×</a> + <a id="side-nav-list" routerLink="/list" (click)="sidenav.close()">{{ uitextSidenavNewCalc }}</a> <a id="side-nav-load-session" (click)="loadSession()">{{ uitextSidenavLoadSession }}</a> - <a id="side-nav-setup" routerLink="/setup" (click)="closeNav()">{{ uitextSidenavParams }}</a> - <a id="side-nav-help" target="_blank" href="assets/docs-fr/" (click)="closeNav()">{{ uitextSidenavHelp }}</a> - <div class="hyd_fillvertical"></div> + <a id="side-nav-setup" routerLink="/setup" (click)="sidenav.close()">{{ uitextSidenavParams }}</a> + <a id="side-nav-help" target="_blank" href="assets/docs-fr/" (click)="sidenav.close()">{{ uitextSidenavHelp }}</a> + <div class="hyd_version"> JaLHyd version: {{ getDateRevision()[0] }}<br/> ngHyd version: {{ getDateRevision()[1] }} </div> </div> - </div> - </div> - - <!-- chargement des calculettes --> - <div appLoadCalcDialogAnchor></div> + </mat-sidenav> - <!-- sauvegarde des calculettes --> - <div appSaveCalcDialogAnchor></div> + <mat-sidenav-content fxFlexFill> + <!-- chargement des calculettes --> + <div appLoadCalcDialogAnchor></div> + + <!-- sauvegarde des calculettes --> + <div appSaveCalcDialogAnchor></div> + + <div class="container-fluid"> + <div class="row"> + <div class="col-12"> + <router-outlet (activate)="onRouterOutletActivated($event)"></router-outlet> + </div> + </div> + </div> + </mat-sidenav-content> - <!-- règle gradée des colonnes Bootstrap --> - <div *ngIf="ruler" class="container-fluid"> - <div class="row"> - <div class="col 1 red">1</div> - <div class="col 1 green">2</div> - <div class="col 1 blue">3</div> - <div class="col 1 red">4</div> - <div class="col 1 green">5</div> - <div class="col 1 blue">6</div> - <div class="col 1 red">7</div> - <div class="col 1 green">8</div> - <div class="col 1 blue">9</div> - <div class="col 1 red">10</div> - <div class="col 1 green">11</div> - <div class="col 1 blue">12</div> - </div> - </div> + </mat-sidenav-container> - <div class="container-fluid"> - <div class="row"> - <div class="col-12"> - <router-outlet (activate)="onRouterOutletActivated($event)"></router-outlet> - </div> - </div> - </div> </main> <footer> @@ -88,4 +78,6 @@ <br> <a href="http://www.irstea.fr/">Institut national de recherche en sciences et technologies pour l'environnement et l'agriculture</a> </div> --> -</footer> \ No newline at end of file +</footer> + +</app-layout> diff --git a/src/app/app.component.scss b/src/app/app.component.scss index 9a709694f..8262d7966 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -3,67 +3,121 @@ background-image: none; } -// sidenav -body { - font-family: "Lato", sans-serif; +button:focus { + outline: 0; } -.sidenav { - height: 100%; - width: 0; - position: fixed; - z-index: 2000; - top: 0; - left: 0; - background-color: #111; - overflow-x: hidden; - transition: 0.5s; - padding-top: 60px; - display: flex; - flex-direction: column; +#main-toolbar { + height: 54px; } -.sidenav a, .sidenav div.hyd_version { - padding: 8px 8px 8px 32px; - text-decoration: none; - font-size: 16px; - color: #aaaaaa; - display: block; - transition: 0.3s; +#open-menu mat-icon, #new-calculator mat-icon { + transform: scale(1.6); } -.sidenav div.hyd_version { - color: #888888; - padding: 0px 32px 0px 0px; - text-align: right; -} +#new-calculator { + cursor: pointer; + position: absolute; + right: 10px; -.sidenav div.hyd_fillvertical { - flex: 1; + &:focus { + outline: 0; + } } -.sidenav a:hover { - color: #f1f1f1; +#tabs-container { + width: 100%; } -.sidenav .closebtn { - position: absolute; - top: 0; - right: 25px; - font-size: 36px; - margin-left: 50px; -} +.calculator-button { + width: 11%; /* 8 tabs */ + @media screen and (max-width: 1200px) { + width: 14%; /* 6 tabs */ + } + @media screen and (max-width: 800px) { + width: 21%; /* 4 tabs */ + } + @media screen and (max-width: 640px) { + width: 27%; /* 3 tabs */ + } + @media screen and (max-width: 480px) { + width: 24%; /* 3 tabs */ + } + @media screen and (max-width: 320px) { + width: 35%; /* 2 tabs */ + } + /* tabs-looking buttons */ + margin-right: 3px; + margin-top: 11px; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + border-bottom: solid #3F51B5 2px; /* @TODO */ + + &.active { + border-bottom-color: white; + + .calc-type { + color: #777; + } + } + + .calc-name { + display: block; + margin-top: -3px; /* ark ! */ + } -.navbar-brand { - color: #fff; + .calc-type { + display: block; + font-size: 0.7em; + color: #bbb; + line-height: 24px; + margin-top: -14px; /* ark ! */ + } } -nav div.container { - display: contents; + +// sidenav + +.sidenav { + height: 100%; + background-color: #111; + overflow-x: hidden; + padding-top: 60px; + padding-right: 4em; + + a, .div.hyd_version { + padding: 8px 8px 8px 32px; + text-decoration: none; + font-size: 16px; + color: #aaaaaa; + display: block; + transition: 0.3s; + } + + div.hyd_version { + position: absolute; + bottom: 0; + right: 0; + font-size: 0.8em; + color: #888888; + padding: 0 2em 1em 0; + text-align: right; + } + + a:hover { + color: #f1f1f1; + } + + .closebtn { + position: absolute; + top: 0; + right: 25px; + font-size: 36px; + margin-left: 50px; + } } -@media screen and (max-height: 450px) { +/*@media screen and (max-height: 450px) { .sidenav {padding-top: 15px;} .sidenav a {font-size: 18px;} -} -// sidenav \ No newline at end of file +}*/ diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 811356e82..b334397c8 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,5 +1,5 @@ import { Component, ApplicationRef, OnInit, OnDestroy, HostListener, ViewChild, ComponentRef } from "@angular/core"; -import { Router, ActivatedRoute } from "@angular/router"; +import { Router, Event, NavigationEnd } from "@angular/router"; import { Observer, jalhydDateRev } from "jalhyd"; @@ -18,6 +18,8 @@ import { SaveCalcDialogAnchorDirective } from "./components/save-calculator/save import { SaveCalculatorComponent } from "./components/save-calculator/save-calculator.component"; import { nghydDateRev } from "../date_revision"; +import { MatSidenav, MatToolbar } from "@angular/material"; + @Component({ selector: "nghyd-app", @@ -26,6 +28,13 @@ import { nghydDateRev } from "../date_revision"; providers: [ErrorService] }) export class AppComponent implements OnInit, OnDestroy, Observer { + + @ViewChild("sidenav") + public sidenav: MatSidenav; + + @ViewChild("navbar") + public navbar: MatToolbar; + /** * liste des calculettes. Forme des objets : * "title": nom de la calculette @@ -39,6 +48,8 @@ export class AppComponent implements OnInit, OnDestroy, Observer { */ private _currentFormId: number; + private _innerWidth; + @ViewChild(LoadCalcDialogAnchorDirective) private appLoadCalcDialogAnchor: LoadCalcDialogAnchorDirective; @@ -65,19 +76,21 @@ export class AppComponent implements OnInit, OnDestroy, Observer { ServiceFactory.instance.internationalisationService = intlService; ServiceFactory.instance.formulaireService = formulaireService; ServiceFactory.instance.httpService = httpService; - } - // process.on('unhandledRejection', (reason, p) => { - // console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); - // // Stack Trace - // console.log(reason.stack); - // }); + this.router.events.subscribe((event: Event) => { + // close side navigation when clicking a calculator tab + if (event instanceof NavigationEnd) { + this.sidenav.close(); + } + }); + } ngOnInit() { this.intlService.addObserver(this); this.intlService.setLocale("fr"); this.formulaireService.addObserver(this); this.subscribeErrorService(); + this._innerWidth = window.innerWidth; } ngOnDestroy() { @@ -85,6 +98,11 @@ export class AppComponent implements OnInit, OnDestroy, Observer { this.formulaireService.removeObserver(this); } + @HostListener("window:resize", ["$event"]) + onResize(event) { + this._innerWidth = window.innerWidth; + } + public get uitextSidenavNewCalc() { return this.intlService.localizeText("INFO_MENU_NOUVELLE_CALC"); } @@ -105,6 +123,40 @@ export class AppComponent implements OnInit, OnDestroy, Observer { return this._calculators; } + public setActiveCalc(uid: string) { + this._calculators.forEach((calc) => { + calc.active = (calc.uid === uid); + }); + } + + /** + * Returns true if sum of open calculator tabs witdh is lower than navbar + * available space (ie. if navbar is not overflowing), false otherwise + */ + public get tabsFitInNavbar() { + // manual breakpoints + // @WARNING keep in sync with .calculator-buttons sizes in app.component.scss + let tabsLimit = 2; + if (this._innerWidth > 320) { + tabsLimit = 3; + } + if (this._innerWidth > 480) { + tabsLimit = 3; + } + if (this._innerWidth > 640) { + tabsLimit = 4; + } + if (this._innerWidth > 800) { + tabsLimit = 6; + } + if (this._innerWidth > 1200) { + tabsLimit = 8; + } + + const fits = this._calculators.length <= tabsLimit; + return fits; + } + /** * abonnement au service d'erreurs */ @@ -145,9 +197,12 @@ export class AppComponent implements OnInit, OnDestroy, Observer { this._calculators.push( { "title": f.calculatorName, - "uid": String(f.uid) + "type": this.formulaireService.getLocalisedTitleFromCalculatorType(f.calculatorType), + "uid": f.uid } ); + this.setActiveCalc(f.uid); + this._tabsChanged = true; // abonnement en tant qu'observateur du nouveau formulaire f.addObserver(this); @@ -201,6 +256,8 @@ export class AppComponent implements OnInit, OnDestroy, Observer { private updateCalculatorTitle(f: FormulaireDefinition, title: string) { const formIndex = this.getCalculatorIndexFromId(f.uid); this._calculators[formIndex]["title"] = title; + + this._tabsChanged = true; // recompute size ! } /** @@ -267,7 +324,6 @@ export class AppComponent implements OnInit, OnDestroy, Observer { * - celle après celle supprimée * - ou celle avant celle supprimée si on supprime la dernière */ - let newId = null; const l = this._calculators.length; if (l > 1) { @@ -286,6 +342,7 @@ export class AppComponent implements OnInit, OnDestroy, Observer { // MAJ affichage + this._tabsChanged = true; if (newId === null) { this.toList(); this._currentFormId = null; @@ -298,8 +355,9 @@ export class AppComponent implements OnInit, OnDestroy, Observer { this.router.navigate(["/list"]); } - private toCalc(id: number) { + private toCalc(id: string) { this.router.navigate(["/calculator", id]); + this.setActiveCalc(id); } /** @@ -320,19 +378,7 @@ export class AppComponent implements OnInit, OnDestroy, Observer { return false; } - // sidenav - - public openNav() { - document.getElementById("mySidenav").style.width = "300px"; - } - - public closeNav() { - document.getElementById("mySidenav").style.width = "0"; - } - public loadSession() { - this.closeNav(); - // création du dialogue de sélection des formulaires à sauver const compRef: ComponentRef<LoadCalculatorComponent> = this.appLoadCalcDialogAnchor.createDialog(); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 0e611c908..3a23b6432 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,6 +1,8 @@ import { BrowserModule } from "@angular/platform-browser"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core"; +import { MatButtonModule, MatCheckboxModule, MatIconModule, MatTabsModule, MatSidenavModule, MatToolbarModule } from "@angular/material"; +import { FlexLayoutModule } from "@angular/flex-layout"; import { MDBBootstrapModule } from "angular-bootstrap-md"; import { HttpClientModule } from "@angular/common/http"; import { FormsModule } from "@angular/forms"; // <-- NgModel lives here @@ -70,11 +72,16 @@ const appRoutes: Routes = [ BrowserModule, NgxMdModule.forRoot(), BrowserAnimationsModule, + MatButtonModule, + MatCheckboxModule, + MatIconModule, + MatSidenavModule, + MatTabsModule, + MatToolbarModule, MDBBootstrapModule.forRoot(), FormsModule, // <-- import the FormsModule before binding with [(ngModel)] HttpClientModule, - // MdInputModule, - // MdDialogModule, + FlexLayoutModule, ChartModule ], declarations: [ // composants, pipes et directives diff --git a/src/app/components/calculator-list/calculator-list.component.css b/src/app/components/calculator-list/calculator-list.component.css index 61c95428b..107f36397 100644 --- a/src/app/components/calculator-list/calculator-list.component.css +++ b/src/app/components/calculator-list/calculator-list.component.css @@ -10,3 +10,7 @@ min-height: 100%; height: 100%; } + +button:focus { + outline: 0; +} diff --git a/src/app/formulaire/definition/form-definition.ts b/src/app/formulaire/definition/form-definition.ts index a8a00aaa6..5272f0772 100644 --- a/src/app/formulaire/definition/form-definition.ts +++ b/src/app/formulaire/definition/form-definition.ts @@ -70,8 +70,7 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs this.notifyObservers({ "action": "nameChanged", "name": name - }, - this); + }, this); } public get jsonConfig(): {} { diff --git a/src/app/formulaire/formulaire-element.ts b/src/app/formulaire/formulaire-element.ts index 6933766e0..306b9bc63 100644 --- a/src/app/formulaire/formulaire-element.ts +++ b/src/app/formulaire/formulaire-element.ts @@ -69,7 +69,7 @@ export abstract class FormulaireElement extends FormulaireNode { } public getKids(): FormulaireElement[] { - return super.kids as FormulaireElement[]; + return this.kids as FormulaireElement[]; } /** diff --git a/src/index.html b/src/index.html index 3f118aead..3923b320b 100644 --- a/src/index.html +++ b/src/index.html @@ -10,8 +10,8 @@ <!-- ressources pour faire fonctionner le toggler de la navbar --> <!-- <script src="https://code.jquery.com/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n" --> - <script href="dependencies/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n" - crossorigin="anonymous"></script> + <!--script href="dependencies/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n" + crossorigin="anonymous"></script>--> <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb" --> <script href="dependencies/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb" @@ -24,13 +24,14 @@ <!-- Load the Angular Material stylesheet --> <!-- <link href="https://unpkg.com/@angular/material/prebuilt-themes/indigo-pink.css" rel="stylesheet"> - <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <style> body { font-family: Roboto, Arial, sans-serif; } </style> --> + <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> + <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet"> </head> <body> diff --git a/src/main.ts b/src/main.ts index 6373f498c..9b8fff7b9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,3 +1,4 @@ +import "hammerjs"; import { enableProdMode } from "@angular/core"; import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; diff --git a/src/styles.scss b/src/styles.scss index 90d4ee007..aafdbe2db 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1 +1,4 @@ /* You can add global styles to this file, and also import other style files */ +@import "~@angular/material/prebuilt-themes/indigo-pink.css"; +html, body { height: 100%; } +body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } -- GitLab