From 0d8b5cac651a575f132aa7666ae0b34e2b7fdeec Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 18 Jul 2019 16:32:00 +0200
Subject: [PATCH 1/2] Fix #237

---
 src/app/app.module.ts                         |  2 +
 .../calculator.component.html                 | 24 ++++---
 .../calculator.component.scss                 |  6 ++
 .../calculator.component.ts                   | 10 ++-
 .../pab-results-table.component.scss          |  1 +
 .../pab-results/pab-results.component.html    |  3 +
 .../quicknav/quicknav.component.html          |  7 ++
 .../quicknav/quicknav.component.scss          | 67 +++++++++++++++++++
 .../components/quicknav/quicknav.component.ts | 67 +++++++++++++++++++
 src/locale/messages.en.json                   |  3 +
 src/locale/messages.fr.json                   |  3 +
 src/theme.scss                                | 12 ++++
 12 files changed, 196 insertions(+), 9 deletions(-)
 create mode 100644 src/app/components/quicknav/quicknav.component.html
 create mode 100644 src/app/components/quicknav/quicknav.component.scss
 create mode 100644 src/app/components/quicknav/quicknav.component.ts

diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 19cc15e1e..9deea03e4 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -83,6 +83,7 @@ import { SelectModelFieldLineComponent } from "./components/select-model-field-l
 import { PabProfileGraphComponent } from "./components/pab-profile-graph/pab-profile-graph.component";
 import { PabTableComponent } from "./components/pab-table/pab-table.component";
 import { PabVariableResultsSelectorComponent } from "./components/pab-results/pab-variable-results-selector.component";
+import { QuicknavComponent } from "./components/quicknav/quicknav.component";
 
 import { DialogConfirmEmptySessionComponent } from "./components/dialog-confirm-empty-session/dialog-confirm-empty-session.component";
 import { DialogConfirmCloseCalcComponent } from "./components/dialog-confirm-close-calc/dialog-confirm-close-calc.component";
@@ -195,6 +196,7 @@ const appRoutes: Routes = [
     ParamFieldLineComponent,
     ParamLinkComponent,
     ParamValuesComponent,
+    QuicknavComponent,
     RemousResultsComponent,
     ResultsGraphComponent,
     SectionCanvasComponent,
diff --git a/src/app/components/generic-calculator/calculator.component.html b/src/app/components/generic-calculator/calculator.component.html
index 08366714a..87c73d874 100644
--- a/src/app/components/generic-calculator/calculator.component.html
+++ b/src/app/components/generic-calculator/calculator.component.html
@@ -29,6 +29,8 @@
 
     </mat-card-header>
 
+    <quicknav [fxHide.gt-sm]="! isPAB" [items]="quicknavItems" [currentItem]="'input'" [align]="'left'"></quicknav>
+
     <form>
 
         <mat-card-content>
@@ -43,7 +45,8 @@
                 <!-- chapitres -->
                 <mat-card id="calc-card-field-sets"
                   [class.pab-field-sets]="isPAB"
-                  [fxFlex.gt-xs]="isPAB ? '1 0 auto' : '1 0 400px'"
+                  [fxFlex.gt-sm]="isPAB ? '1 0 auto' : '1 0 400px'"
+                  [fxFlex.lt-md]="isPAB ? '1 0 auto' : '1 0 500px'"
                   [fxFlex.lt-sm]="isPAB ? '1 0 auto' : '1 0 300px'">
 
                     <ng-template ngFor let-fe [ngForOf]="formElements">
@@ -73,20 +76,25 @@
                 <!-- résultats -->
                 <mat-card id="calc-card-results"
                   [class.pab-results]="isPAB"
-                  [fxFlex.gt-xs]="isPAB ? '1 0 auto' : '1 0 400px'"
+                  [fxFlex.gt-sm]="isPAB ? '1 0 auto' : '1 0 400px'"
+                  [fxFlex.lt-md]="isPAB ? '1 0 auto' : '1 0 500px'"
                   [fxFlex.lt-sm]="isPAB ? '1 0 auto' : '1 0 300px'">
 
                     <div id="fake-results-anchor"></div>
-                    <mat-card-header>
+
+                    <quicknav [ngClass.lt-xs]="'extraSmall'" [fxHide.gt-sm]="! isPAB" [items]="quicknavItems" [currentItem]="'results'" [align]="'left'"></quicknav>
+
+                    <mat-card-header *ngIf="! isPAB" [fxHide.lt-md]="! isPAB">
                         <mat-card-title>
                             <h1 [innerHTML]="uitextResultsTitle"></h1>
                         </mat-card-title>
-                        <div fxFlex></div>
-                        <button mat-raised-button color="accent" id="generate-pab" *ngIf="isPABCloisons" (click)="generatePAB()"
-                            [disabled]="! generatePABEnabled" [title]="uitextGeneratePabTitle">
-                            {{ uitextGeneratePAB }}
-                        </button>
                     </mat-card-header>
+
+                    <button mat-raised-button color="accent" id="generate-pab" *ngIf="isPABCloisons" (click)="generatePAB()"
+                        [disabled]="! generatePABEnabled" [title]="uitextGeneratePabTitle">
+                        {{ uitextGeneratePAB }}
+                    </button>
+
                     <mat-card-content>
                         <calc-results id="resultsComp" (afterViewChecked)="onCalcResultsViewChecked()"></calc-results>
                     </mat-card-content>
diff --git a/src/app/components/generic-calculator/calculator.component.scss b/src/app/components/generic-calculator/calculator.component.scss
index e1e07ee87..86ee92752 100644
--- a/src/app/components/generic-calculator/calculator.component.scss
+++ b/src/app/components/generic-calculator/calculator.component.scss
@@ -81,4 +81,10 @@ mat-card {
             margin-bottom: 8px;
         }
     }
+
+    mat-card-content {
+        > calc-name {
+            margin-top: 1.5em;
+        }
+    }
 }
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index faec11c44..c72a3f203 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -194,6 +194,14 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
         return this.intlService.localizeText("INFO_CALCULATOR_CLOSE");
     }
 
+    public get quicknavItems() {
+        const elts = [ "input", "results" ];
+        if (this.isPAB) {
+            elts.push("charts");
+        }
+        return elts;
+    }
+
     /**
      * Triggered at calculator instanciation
      */
@@ -279,7 +287,7 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
     }
 
     private scrollToResults() {
-        const element = document.getElementById("fake-results-anchor");
+        const element = document.getElementById ("fake-results-anchor");
         if (element) {
             element.scrollIntoView();
         }
diff --git a/src/app/components/pab-results/pab-results-table.component.scss b/src/app/components/pab-results/pab-results-table.component.scss
index ea1d83f80..efbe2b6ea 100644
--- a/src/app/components/pab-results/pab-results-table.component.scss
+++ b/src/app/components/pab-results/pab-results-table.component.scss
@@ -1,5 +1,6 @@
 :host {
     display: block;
+    margin-bottom: 1.5em;
 }
 
 .pab-results-table-container {
diff --git a/src/app/components/pab-results/pab-results.component.html b/src/app/components/pab-results/pab-results.component.html
index fbb8e9c23..2e31dc126 100644
--- a/src/app/components/pab-results/pab-results.component.html
+++ b/src/app/components/pab-results/pab-results.component.html
@@ -12,6 +12,9 @@
         <pab-results-table *ngIf="hasDisplayableResults" [results]="pabResults"></pab-results-table>
     </div>
 
+    <quicknav *ngIf="hasDisplayableResults" [items]="[ 'input', 'results', 'charts' ]"
+        [currentItem]="'charts'" [align]="'left'"></quicknav>
+
     <div id="pab-graphs-container" class="container" fxLayout="row wrap" fxLayoutAlign="space-around start">
         <pab-profile-graph *ngIf="hasDisplayableResults" fxFlex.gt-xs="1 0 400px" fxFlex.lt-sm="1 0 300px">
         </pab-profile-graph>
diff --git a/src/app/components/quicknav/quicknav.component.html b/src/app/components/quicknav/quicknav.component.html
new file mode 100644
index 000000000..31f1db00b
--- /dev/null
+++ b/src/app/components/quicknav/quicknav.component.html
@@ -0,0 +1,7 @@
+<div class="quicknav-row" [ngStyle]="alignStyle" *ngIf="hasItems" [id]="id"
+    [class.autoPadLeft]="align === 'left'" [class.autoPadRight]="align === 'right'">
+
+    <div class="quicknav-item" *ngFor="let i of items" [class.current]="isCurrent(i)" [ngStyle]="paddingStyle">
+        <a (click)="scrollTo(i)">{{ label(i) }}</a>
+    </div>
+</div>
diff --git a/src/app/components/quicknav/quicknav.component.scss b/src/app/components/quicknav/quicknav.component.scss
new file mode 100644
index 000000000..62391f1ae
--- /dev/null
+++ b/src/app/components/quicknav/quicknav.component.scss
@@ -0,0 +1,67 @@
+:host {
+    display: block;
+    margin-bottom: .5em;
+    width: 100%;
+
+    &.extraSmall {
+
+        .quicknav-item {
+            font-size: 12px;
+
+            &.current {
+                font-size: 18px;
+            }
+        }
+    }
+}
+
+@import "../../../theme.scss";
+
+.quicknav-row {
+    width: 100%;
+    padding-bottom: 3px;
+    border-bottom: solid #dddddd 1px;
+}
+
+.quicknav-item {
+    display: inline;
+    padding: 0 .5em 3px .5em;
+    text-transform: uppercase;
+    font-size: 18px;
+    font-weight: normal;
+
+    &.current {
+        @extend .border-accent;
+        border-bottom-width: 2px;
+        border-bottom-style: solid;
+        font-size: 24px;
+
+        a {
+            // font-weight: bold;
+            @extend .color-accent;
+        }
+    }
+
+    a {
+        cursor: pointer;
+        color: #cbcbcb;
+        &:focus {
+            outline: none;
+        }
+        &:hover {
+            color: #707070;
+        }
+    }
+}
+
+.autoPadLeft {
+    .quicknav-item:first-of-type {
+        padding-left: 0;
+    }
+}
+
+.autoPadRight {
+    .quicknav-item:last-of-type {
+        padding-right: 0;
+    }
+}
diff --git a/src/app/components/quicknav/quicknav.component.ts b/src/app/components/quicknav/quicknav.component.ts
new file mode 100644
index 000000000..17e442ef9
--- /dev/null
+++ b/src/app/components/quicknav/quicknav.component.ts
@@ -0,0 +1,67 @@
+import { Component, Input } from "@angular/core";
+
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
+
+@Component({
+    selector: "quicknav",
+    templateUrl: "./quicknav.component.html",
+    styleUrls: [
+        "./quicknav.component.scss"
+    ]
+})
+export class QuicknavComponent {
+
+    @Input()
+    public items: string[];
+
+    @Input()
+    public currentItem: string;
+
+    @Input()
+    public align: string;
+
+    private prefix = "qn_";
+
+    public constructor(
+        private i18nService: I18nService
+    ) {
+        this.items = [];
+        this.currentItem = "";
+        this.align = "center";
+    }
+
+    public get id() {
+        return this.prefix + this.currentItem;
+    }
+
+    public get hasItems() {
+        return this.items.length > 0;
+    }
+
+    public get alignStyle() {
+        return {
+            "text-align": this.align
+        };
+    }
+
+    public label(item: string = "") {
+        return this.i18nService.localizeText("INFO_QUICKNAV_" + item.toUpperCase());
+    }
+
+    public isCurrent(item: string) {
+        return item === this.currentItem;
+    }
+
+    public scrollTo(item: string) {
+        // https://stackoverflow.com/a/56391657/5986614
+        const element = document.getElementById(this.prefix + item);
+        if (element) {
+            const yCoordinate = element.getBoundingClientRect().top + window.pageYOffset;
+            // element.scrollIntoView({behavior: "smooth", block: "start", inline: "nearest"});
+            window.scrollTo({
+                top: yCoordinate - 60, // substract a little more than navbar height
+                behavior: "smooth"
+            });
+        }
+    }
+}
diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index c83ba6b83..b1f8ab601 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -317,6 +317,9 @@
     "INFO_PARAMFIELD_VARIATED": "Variated",
     "INFO_PARAMMODE_LIST": "Values list",
     "INFO_PARAMMODE_MINMAX": "Min/max",
+    "INFO_QUICKNAV_CHARTS": "charts",
+    "INFO_QUICKNAV_INPUT": "input",
+    "INFO_QUICKNAV_RESULTS": "results",
     "WARNING_PROBLEMS_ENCOUNTERED": "Problems occurred during calculation (info: %info%, warning: %warning%, error: %error%)",
     "INFO_REGIMEUNIFORME_TITRE_COURT": "Uniform flow",
     "INFO_REGIMEUNIFORME_TITRE": "Uniform flow calculation",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index 86203c1bc..5d4dca279 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -317,6 +317,9 @@
     "INFO_PARAMFIELD_VARIATED": "Varié",
     "INFO_PARAMMODE_LIST": "Liste de valeurs",
     "INFO_PARAMMODE_MINMAX": "Min/max",
+    "INFO_QUICKNAV_CHARTS": "graphiques",
+    "INFO_QUICKNAV_INPUT": "données",
+    "INFO_QUICKNAV_RESULTS": "résultats",
     "WARNING_PROBLEMS_ENCOUNTERED": "Des problèmes sont survenus durant le calcul (info: %info%, avertissement: %warning%, erreur: %error%)",
     "INFO_REGIMEUNIFORME_TITRE_COURT": "R. uniforme",
     "INFO_REGIMEUNIFORME_TITRE": "Régime uniforme",
diff --git a/src/theme.scss b/src/theme.scss
index 951487e70..982a741bb 100644
--- a/src/theme.scss
+++ b/src/theme.scss
@@ -204,6 +204,9 @@ $warn: map-get($nghyd-theme, warn);
 .bg-primary-extralight {
     background-color: mat-color($primary, 50);
 }
+.border-primary {
+    border-color: mat-color($primary);
+}
 
 .color-accent {
     color: mat-color($accent);
@@ -223,6 +226,9 @@ $warn: map-get($nghyd-theme, warn);
 .bg-accent-extralight {
     background-color: mat-color($accent, 50);
 }
+.border-accent {
+    border-color: mat-color($accent);
+}
 
 .color-warn {
     color: mat-color($warn);
@@ -242,6 +248,9 @@ $warn: map-get($nghyd-theme, warn);
 .bg-warn-extralight {
     background-color: mat-color($warn, 50);
 }
+.border-warn {
+    border-color: mat-color($warn);
+}
 
 .color-error {
     color: mat-color($nghyd-error);
@@ -249,6 +258,9 @@ $warn: map-get($nghyd-theme, warn);
 .bg-error {
     background-color: mat-color($nghyd-error);
 }
+.border-error {
+    border-color: mat-color($nghyd-error);
+}
 
 .mat-button-toggle-checked {
     background-color: mat-color($accent);
-- 
GitLab


From 59b61ba6875b26f842ad150ca5e2ba1317d92894 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Mon, 22 Jul 2019 12:31:12 +0200
Subject: [PATCH 2/2] Quicknav : show "charts" entry only if PAB has results

---
 src/app/components/generic-calculator/calculator.component.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index c72a3f203..623091c90 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -196,7 +196,7 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
 
     public get quicknavItems() {
         const elts = [ "input", "results" ];
-        if (this.isPAB) {
+        if (this.isPAB && this.hasResults) {
             elts.push("charts");
         }
         return elts;
-- 
GitLab