From 212f4e9d012e5dac4b546739044a743dfc03af9d Mon Sep 17 00:00:00 2001 From: "mathias.chouet" <mathias.chouet@irstea.fr> Date: Thu, 24 Jan 2019 11:51:28 +0100 Subject: [PATCH] angular-material MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit navbar et sidenav fixes navbar: les onglets s'écroulent en un menu déroulant sidenav: ajout d'icônes sidenav: ajout sauvegarde de session --- src/app/app.component.html | 55 ++++++-- src/app/app.component.scss | 133 ++++++++++++++---- src/app/app.component.ts | 117 ++++++++------- src/app/app.module.ts | 5 +- .../services/formulaire/formulaire.service.ts | 2 +- src/locale/messages.en.json | 3 +- src/locale/messages.fr.json | 1 + 7 files changed, 222 insertions(+), 94 deletions(-) diff --git a/src/app/app.component.html b/src/app/app.component.html index b1dd20bba..5d9b30e8a 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -11,21 +11,43 @@ <a class="navbar-brand"></a> + <!-- calculators list as a dropdown menu--> <div [hidden]="tabsFitInNavbar"> - ICI LE SUPER SELECT DE LA MORT + + <button *ngIf="currentCalc" mat-button [matMenuTriggerFor]="menu" color="primary" class="calculators-menu-title"> + <mat-icon class="dropdown-icon">arrow_drop_down</mat-icon> + <span class="calc-name"> + {{ currentCalc.title }} + </span> + <span class="calc-type" [innerHTML]="'( ' + currentCalc ? currentCalc.type : '' + ' )'"></span> + </button> + <mat-menu #menu="matMenu" colo="accent"> + <button mat-menu-item *ngFor="let c of calculators" class="calculator-menu-item" + [routerLink]="['/calculator/',c.uid]" [color]="c.active ? '' : 'primary'" [class.active]="c.active" + (click)="setActiveCalc(c.uid)"> + + <span class="calc-name"> + {{ c.title }} + </span> + <span class="calc-type" [innerHTML]="'( ' + c.type + ' )'"></span> + </button> + </mat-menu> </div> + + <!-- calculators list as a tabs bar--> <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"> + (click)="setActiveCalc(c.uid)"> <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> + <button mat-icon-button id="new-calculator" routerLink="/list" (click)="sidenav.close()"> <mat-icon>add_box</mat-icon> </button> @@ -40,10 +62,27 @@ <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)="sidenav.close()">{{ uitextSidenavParams }}</a> - <a id="side-nav-help" target="_blank" href="assets/docs-fr/" (click)="sidenav.close()">{{ uitextSidenavHelp }}</a> + + <a id="side-nav-list" routerLink="/list" (click)="sidenav.close()"> + <mat-icon>add</mat-icon> + {{ uitextSidenavNewCalc }} + </a> + <a id="side-nav-load-session" (click)="loadSession(); sidenav.close()"> + <mat-icon>folder_open</mat-icon> + {{ uitextSidenavLoadSession }} + </a> + <a id="side-nav-save-session" (click)="saveForm(); sidenav.close()"> + <mat-icon>save_alt</mat-icon> + {{ uitextSidenavSaveSession }} + </a> + <a id="side-nav-setup" routerLink="/setup" (click)="sidenav.close()"> + <mat-icon>settings</mat-icon> + {{ uitextSidenavParams }} + </a> + <a id="side-nav-help" target="_blank" href="assets/docs-fr/" (click)="sidenav.close()"> + <mat-icon>help</mat-icon> + {{ uitextSidenavHelp }} + </a> <div class="hyd_version"> JaLHyd version: {{ getDateRevision()[0] }}<br/> @@ -52,7 +91,7 @@ </div> </mat-sidenav> - <mat-sidenav-content fxFlexFill> + <mat-sidenav-content class="sidenav-content" fxFlexFill> <!-- chargement des calculettes --> <div appLoadCalcDialogAnchor></div> diff --git a/src/app/app.component.scss b/src/app/app.component.scss index 8262d7966..8fcb2d8db 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -1,3 +1,9 @@ +// variables + +$navbar_height: 54px; + +// rules + .dropdown-menu > li > a:hover { background-color: rgb(172, 172, 172); background-image: none; @@ -8,7 +14,9 @@ button:focus { } #main-toolbar { - height: 54px; + position: fixed; + height: $navbar_height; + z-index: 200; } #open-menu mat-icon, #new-calculator mat-icon { @@ -30,28 +38,88 @@ button:focus { } .calculator-button { - width: 11%; /* 8 tabs */ + /*width: 11%; // 8 tabs + @media screen and (max-width: 1200px) { + width: 14.5%; // 6 tabs + }*/ + width: 15.8%; // 6 larger tabs @media screen and (max-width: 1200px) { - width: 14%; /* 6 tabs */ + width: 14.5%; // 6 tabs } @media screen and (max-width: 800px) { - width: 21%; /* 4 tabs */ + width: 21%; // 4 tabs } @media screen and (max-width: 640px) { - width: 27%; /* 3 tabs */ + width: 27%; // 3 larger tabs } @media screen and (max-width: 480px) { - width: 24%; /* 3 tabs */ + width: 24%; // 3 tabs } - @media screen and (max-width: 320px) { - width: 35%; /* 2 tabs */ - } - /* tabs-looking buttons */ + // 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 */ + border-bottom: solid #3F51B5 4px; + + &.active { + border-bottom-color: white; + box-shadow: none; + + .calc-type { + color: #777; + } + } + + .calc-name { + display: block; + margin-top: -3px; // ark ! + text-overflow: ellipsis; + overflow: hidden; + } + + .calc-type { + display: block; + font-size: 0.7em; + color: #bbb; + line-height: 24px; + margin-top: -14px; // ark ! + text-overflow: ellipsis; + overflow: hidden; + } +} + +.calculators-menu-title { + background-color: white; + width: 360px; // minimal screen width supported: 480 + margin-top: 8px; + + .dropdown-icon { + float: right; + transform: scale(2); + margin-top: 8px; + margin-left: 10px; + } + + .calc-name { + display: block; + margin-top: -3px; // ark ! + text-overflow: ellipsis; + overflow: hidden; + } + + .calc-type { + display: block; + font-size: 0.7em; + color: #777; + line-height: 24px; + margin-top: -14px; // ark ! + text-overflow: ellipsis; + overflow: hidden; + } +} + +.calculator-button { &.active { border-bottom-color: white; @@ -63,7 +131,9 @@ button:focus { .calc-name { display: block; - margin-top: -3px; /* ark ! */ + margin-top: -3px; // ark ! + text-overflow: ellipsis; + overflow: hidden; } .calc-type { @@ -71,7 +141,9 @@ button:focus { font-size: 0.7em; color: #bbb; line-height: 24px; - margin-top: -14px; /* ark ! */ + margin-top: -14px; // ark ! + text-overflow: ellipsis; + overflow: hidden; } } @@ -79,19 +151,29 @@ button:focus { // sidenav .sidenav { + position:fixed; height: 100%; background-color: #111; overflow-x: hidden; - padding-top: 60px; + padding-top: 60px + $navbar_height; padding-right: 4em; + z-index: 100; a, .div.hyd_version { - padding: 8px 8px 8px 32px; + padding: 8px 8px 8px 24px; text-decoration: none; font-size: 16px; color: #aaaaaa; display: block; transition: 0.3s; + + &:focus { + outline: 0; + } + + &:hover { + color: #f1f1f1; + } } div.hyd_version { @@ -104,20 +186,23 @@ button:focus { text-align: right; } - a:hover { - color: #f1f1f1; - } - .closebtn { position: absolute; - top: 0; + top: 54px; right: 25px; font-size: 36px; margin-left: 50px; } + + mat-icon { + color: #fff; + vertical-align: bottom; + margin-right: 6px; + } } -/*@media screen and (max-height: 450px) { - .sidenav {padding-top: 15px;} - .sidenav a {font-size: 18px;} -}*/ +// page contents + +.sidenav-content { + margin-top: $navbar_height; +} \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index b334397c8..3ba18abb1 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -35,6 +35,9 @@ export class AppComponent implements OnInit, OnDestroy, Observer { @ViewChild("navbar") public navbar: MatToolbar; + /** current calculator, inferred from _currentFormId by setActiveCalc() (used for navbar menu) */ + public currentCalc: any; + /** * liste des calculettes. Forme des objets : * "title": nom de la calculette @@ -44,11 +47,12 @@ export class AppComponent implements OnInit, OnDestroy, Observer { /** * id du formulaire courant - * on utilise pas directement FormulaireService.currentFormId pour éviter l'erreur ExpressionChangedAfterItHasBeenCheckedError + * on utilise pas directement FormulaireService.currentFormId pour éviter l'erreur + * ExpressionChangedAfterItHasBeenCheckedError */ - private _currentFormId: number; + private _currentFormId: string; - private _innerWidth; + private _innerWidth: number; @ViewChild(LoadCalcDialogAnchorDirective) private appLoadCalcDialogAnchor: LoadCalcDialogAnchorDirective; @@ -100,6 +104,7 @@ export class AppComponent implements OnInit, OnDestroy, Observer { @HostListener("window:resize", ["$event"]) onResize(event) { + // keep track of window size for navbar tabs arrangement this._innerWidth = window.innerWidth; } @@ -115,6 +120,10 @@ export class AppComponent implements OnInit, OnDestroy, Observer { return this.intlService.localizeText("INFO_MENU_LOAD_SESSION_TITLE"); } + public get uitextSidenavSaveSession() { + return this.intlService.localizeText("INFO_MENU_SAVE_SESSION_TITLE"); + } + public get uitextSidenavHelp() { return this.intlService.localizeText("INFO_MENU_HELP_TITLE"); } @@ -123,10 +132,17 @@ export class AppComponent implements OnInit, OnDestroy, Observer { return this._calculators; } + public get currentFormId() { + return this._currentFormId; + } + public setActiveCalc(uid: string) { this._calculators.forEach((calc) => { calc.active = (calc.uid === uid); }); + // mark current calc for navbar menu + const index = this.getCalculatorIndexFromId(uid); + this.currentCalc = this._calculators[index]; } /** @@ -136,10 +152,7 @@ export class AppComponent implements OnInit, OnDestroy, Observer { 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; - } + let tabsLimit = 0; if (this._innerWidth > 480) { tabsLimit = 3; } @@ -149,9 +162,9 @@ export class AppComponent implements OnInit, OnDestroy, Observer { if (this._innerWidth > 800) { tabsLimit = 6; } - if (this._innerWidth > 1200) { + /*if (this._innerWidth > 1200) { tabsLimit = 8; - } + }*/ const fits = this._calculators.length <= tabsLimit; return fits; @@ -202,7 +215,6 @@ export class AppComponent implements OnInit, OnDestroy, Observer { } ); this.setActiveCalc(f.uid); - this._tabsChanged = true; // abonnement en tant qu'observateur du nouveau formulaire f.addObserver(this); @@ -213,13 +225,7 @@ export class AppComponent implements OnInit, OnDestroy, Observer { break; case "currentFormChanged": - /* - utilisation de setTimeout() pour éviter le message console ExpressionChangedAfterItHasBeenCheckedError - relatif au getter getHighlightClass() (qui change de valeur quand le formulaire courant change) - */ - setTimeout(() => { - this._currentFormId = data["formId"]; - }, 1); + this._currentFormId = data["formId"]; break; case "saveForm": @@ -256,43 +262,6 @@ 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 ! - } - - /** - * sauvegarde du/des formulaires - * @param form formulaire à sélectionner par défaut dans la liste - */ - private saveForm(form: FormulaireDefinition) { - // création du dialogue de sélection des formulaires à sauver - const compRef: ComponentRef<SaveCalculatorComponent> = this.appSaveCalcDialogAnchor.createDialog(); - - // création de la liste des formulaires - const list = []; - for (const c of this._calculators) { - const uid = c["uid"]; - list.push({ - "selected": uid === form.uid, - "title": c["title"], - "uid": uid - }); - } - - // passage de la liste, récupération d'une Promise pour traiter le résultat - const prom: Promise<any[]> = compRef.instance.run(list); - prom.then(innerList => { - let name = compRef.instance.filename; - - // ajout extension ".json" - const re = /.+\.json/; - const match = re.exec(name.toLowerCase()); - if (match === null) { - name = name + ".json"; - } - - this.saveSession(innerList, name); - }).catch(err => { }); } private saveSession(calcList: any[], filename) { @@ -342,7 +311,6 @@ export class AppComponent implements OnInit, OnDestroy, Observer { // MAJ affichage - this._tabsChanged = true; if (newId === null) { this.toList(); this._currentFormId = null; @@ -367,10 +335,6 @@ export class AppComponent implements OnInit, OnDestroy, Observer { this._routerCurrentComponent = a; } - private getHighlightClass(uid: number) { - return uid === this._currentFormId ? "blue darken-2" : ""; - } - // flag d'affichage des repères des colonnes Bootstrap : uniquement en mode dev // cf. src/environments/*, ng build --env=<mode> (par ex : ng build --env=prod) public get ruler(): boolean { @@ -394,6 +358,41 @@ export class AppComponent implements OnInit, OnDestroy, Observer { return dr; } + /** + * sauvegarde du/des formulaires + * @param form formulaire à sélectionner par défaut dans la liste + */ + public saveForm(form?: FormulaireDefinition) { + // création du dialogue de sélection des formulaires à sauver + const compRef: ComponentRef<SaveCalculatorComponent> = this.appSaveCalcDialogAnchor.createDialog(); + + // création de la liste des formulaires + const list = []; + for (const c of this._calculators) { + const uid = c["uid"]; + list.push({ + "selected": form ? (uid === form.uid) : true, + "title": c["title"], + "uid": uid + }); + } + + // passage de la liste, récupération d'une Promise pour traiter le résultat + const prom: Promise<any[]> = compRef.instance.run(list); + prom.then(innerList => { + let name = compRef.instance.filename; + + // ajout extension ".json" + const re = /.+\.json/; + const match = re.exec(name.toLowerCase()); + if (match === null) { + name = name + ".json"; + } + + this.saveSession(innerList, name); + }).catch(err => { }); + } + /** * détection de la fermeture de la page/navigateur et demande de confirmation */ diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 3a23b6432..0eefd37fd 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,7 +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 { MatButtonModule, MatCheckboxModule, MatIconModule, MatSelectModule, MatTabsModule, MatSidenavModule, + MatToolbarModule, MatMenuModule } from "@angular/material"; import { FlexLayoutModule } from "@angular/flex-layout"; import { MDBBootstrapModule } from "angular-bootstrap-md"; import { HttpClientModule } from "@angular/common/http"; @@ -75,6 +76,8 @@ const appRoutes: Routes = [ MatButtonModule, MatCheckboxModule, MatIconModule, + MatMenuModule, + MatSelectModule, MatSidenavModule, MatTabsModule, MatToolbarModule, diff --git a/src/app/services/formulaire/formulaire.service.ts b/src/app/services/formulaire/formulaire.service.ts index e6570d34f..56d4a30c4 100644 --- a/src/app/services/formulaire/formulaire.service.ts +++ b/src/app/services/formulaire/formulaire.service.ts @@ -160,7 +160,7 @@ export class FormulaireService extends Observable { return f; }).then(fi => { if (jsonState === undefined) { - fi.calculatorName = decode(this.getLocalisedTitleFromCalculatorType(ct) + " (" + fi.uid + ")"); + fi.calculatorName = decode(this.getLocalisedTitleFromCalculatorType(ct)); } fi.initNub(); return fi; diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json index 01e69db11..2c46e4203 100644 --- a/src/locale/messages.en.json +++ b/src/locale/messages.en.json @@ -98,10 +98,11 @@ "INFO_LIB_ZDV": "Crest weir elevation or gate base", "INFO_LIB_ZRAM": "Upstream apron elevation (m)", "INFO_LIB_ZT": "Triangle top elevation (m)", + "INFO_MACRORUGO_TITRE": "Rock-ramp fishpasses", "INFO_MENU_HELP_TITLE": "Help", "INFO_MENU_LOAD_SESSION_TITLE": "Load session", "INFO_MENU_NOUVELLE_CALC": "New calculation module", - "INFO_MACRORUGO_TITRE": "Rock-ramp fishpasses", + "INFO_MENU_SAVE_SESSION_TITLE": "Save session", "INFO_OPTION_NO": "No", "INFO_OPTION_YES": "Yes", "INFO_OUVRAGE": "Structure", diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json index cd700e267..05c3822a9 100644 --- a/src/locale/messages.fr.json +++ b/src/locale/messages.fr.json @@ -100,6 +100,7 @@ "INFO_LIB_ZT": "Cote haute du triangle (m)", "INFO_MENU_HELP_TITLE": "Aide", "INFO_MENU_LOAD_SESSION_TITLE": "Charger une session", + "INFO_MENU_SAVE_SESSION_TITLE": "Enregistrer la session", "INFO_MENU_NOUVELLE_CALC": "Nouveau module de calcul", "INFO_MACRORUGO_TITRE": "Passe à macro-rugosité", "INFO_OPTION_NO": "Non", -- GitLab