diff --git a/src/app/app.module.ts b/src/app/app.module.ts index d7fa9db45fba15a478a7f6bce7564f616ff9023e..3dc37b5aee1e9dda60cf51ed700506360eafccf0 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -86,6 +86,7 @@ import { LogDrawerComponent } from "./components/log-drawer/log-drawer.component import { ParamLinkComponent } from "./components/param-link/param-link.component"; import { PabProfileChartComponent } from "./components/pab-profile-chart/pab-profile-chart.component"; import { PabTableComponent } from "./components/pab-table/pab-table.component"; +import { PbSchemaComponent } from './components/pb-schema/pb-schema.component'; import { VariableResultsSelectorComponent } from "./components/variable-results-selector/variable-results-selector.component"; import { QuicknavComponent } from "./components/quicknav/quicknav.component"; import { ModulesDiagramComponent } from "./components/modules-diagram/modules-diagram.component"; diff --git a/src/app/calculators/prebarrage/config.json b/src/app/calculators/prebarrage/config.json new file mode 100644 index 0000000000000000000000000000000000000000..754fbc81d7914f8c3412fe1fe85ad91b3fb58066 --- /dev/null +++ b/src/app/calculators/prebarrage/config.json @@ -0,0 +1,34 @@ +[ + { + "id": "schema_interactif_pb", + "type": "pb_schema" + }, + { + "id": "fs_params", + "type": "fieldset", + "fields": [ + { + "id": "select_upstream", + "type": "select_reference", + "reference": "nub", + "source": "upstream_stuff" + }, + { + "id": "select_downstream", + "type": "select_reference", + "reference": "nub", + "source": "downstream_stuff" + }, + "Q", + "Z1", + "Z2" + ] + }, + { + "type": "options", + "selectIds": [ ], + "upstreamSelectId": "select_upstream", + "downstreamSelectId": "select_downstream", + "_help": "prebarrage.html" + } +] diff --git a/src/app/calculators/prebarrage/en.json b/src/app/calculators/prebarrage/en.json new file mode 100644 index 0000000000000000000000000000000000000000..a8e5ea027fa13e4b3900508a9240ff713331bb47 --- /dev/null +++ b/src/app/calculators/prebarrage/en.json @@ -0,0 +1,12 @@ +{ + "fs_target": "Target parameter characteristics", + "fs_searched": "Searched parameter characteristics", + + "Ytarget": "Value of target parameter", + "Xinit": "Initial value for searched parameter", + "X": "Value for searched parameter", + + "select_target_nub": "Module and parameter to calculate", + "select_target_result": "Targetted result", + "select_searched_param": "Searched parameter" +} \ No newline at end of file diff --git a/src/app/calculators/prebarrage/fr.json b/src/app/calculators/prebarrage/fr.json new file mode 100644 index 0000000000000000000000000000000000000000..e10e659961bb80b9c634f61af2f2f38c414541fa --- /dev/null +++ b/src/app/calculators/prebarrage/fr.json @@ -0,0 +1,10 @@ +{ + "fs_params": "Édition du bassin / de la cloison", + + "Ytarget": "Valeur du paramètre cible", + "Xinit": "Valeur initiale du paramètre recherché", + "X": "Valeur du paramètre recherché", + + "select_upstream": "Bassin / cloison amont", + "select_downstream": "Bassin / cloison aval" +} \ No newline at end of file diff --git a/src/app/components/calculator-list/calculator-list.component.ts b/src/app/components/calculator-list/calculator-list.component.ts index 6dc05bea1420b7043f7f1a8c3b92b26bc5acf58f..e4c9a47b2c6d4134cea74527eb6d4fc095ca5f97 100644 --- a/src/app/components/calculator-list/calculator-list.component.ts +++ b/src/app/components/calculator-list/calculator-list.component.ts @@ -114,10 +114,14 @@ export class CalculatorListComponent implements OnInit { for (const t of unusedCalculators) { if ( // those sub-Nub types cannot be built outside a parent - t !== CalculatorType.Structure - && t !== CalculatorType.Section - && t !== CalculatorType.CloisonAval - && t !== CalculatorType.YAXN + ! [ + CalculatorType.Structure, + CalculatorType.Section, + CalculatorType.CloisonAval, + CalculatorType.YAXN, + CalculatorType.PbBassin, + CalculatorType.PbCloison + ].includes(t) ) { unusedTheme.calculators.push({ type: t, diff --git a/src/app/components/generic-calculator/calculator.component.html b/src/app/components/generic-calculator/calculator.component.html index f5ccefc0fd6f60664002184281dd7510c3ac9c52..7660f8a21edd34e71ceb395f572d9d98f8469555 100644 --- a/src/app/components/generic-calculator/calculator.component.html +++ b/src/app/components/generic-calculator/calculator.component.html @@ -62,22 +62,42 @@ [fxFlex.lt-md]="isWide ? '1 0 auto' : '1 0 500px'" [fxFlex.lt-sm]="isWide ? '1 0 auto' : '1 0 300px'"> - <ng-template ngFor let-fe [ngForOf]="formElements"> - <field-set *ngIf="isFieldset(fe)" [style.display]="getElementStyleDisplay(fe.id)" [fieldSet]=fe - (radio)=onRadioClick($event) (validChange)=onElementValid() - (inputChange)=onInputChange($event) (tabPressed)="onTabPressed($event)"> - </field-set> - - <fieldset-container *ngIf="isFieldsetContainer(fe)" - [style.display]="getElementStyleDisplay(fe.id)" [_container]=fe (radio)=onRadioClick($event) - (validChange)=onElementValid() (inputChange)=onInputChange($event) - (tabPressed)="onTabPressed($event)"> - </fieldset-container> - - <pab-table *ngIf="isPabTable(fe)" [pabTable]=fe (radio)=onRadioClick($event) - (validChange)=onElementValid() (inputChange)=onInputChange($event)> - </pab-table> - </ng-template> + <div id="calc-card-field-sets-container" [fxLayout]="isPB ? 'row wrap' : 'column'"> + + <ng-template ngFor let-fe [ngForOf]="formElements"> + <field-set *ngIf="isFieldset(fe)" [style.display]="getElementStyleDisplay(fe.id)" [fieldSet]=fe + (radio)=onRadioClick($event) (validChange)=onElementValid() (inputChange)=onInputChange($event) + (tabPressed)="onTabPressed($event)" + [fxFlex.gt-sm]="isPB ? '1 0 400px' : '1 0 auto'" + [fxFlex.lt-md]="isPB ? '1 0 500px' : '1 0 auto'" + [fxFlex.lt-sm]="isPB ? '1 0 300px' : '1 0 auto'"> + </field-set> + + <fieldset-container *ngIf="isFieldsetContainer(fe)" [style.display]="getElementStyleDisplay(fe.id)" [_container]=fe + (radio)=onRadioClick($event) (validChange)=onElementValid() (inputChange)=onInputChange($event) + (tabPressed)="onTabPressed($event)" + fxFlex="1 0 auto"> + </fieldset-container> + + <pab-table *ngIf="isPabTable(fe)" [pabTable]=fe (radio)=onRadioClick($event) + (validChange)=onElementValid() (inputChange)=onInputChange($event) + fxFlex="1 0 auto"> + </pab-table> + + <div *ngIf="isPbSchema(fe)" id="pb-schema-container" + [fxFlex.gt-sm]="isPB ? '1 0 400px' : '1 0 auto'" + [fxFlex.lt-md]="isPB ? '1 0 500px' : '1 0 auto'" + [fxFlex.lt-sm]="isPB ? '1 0 300px' : '1 0 auto'"> + + <pb-schema *ngIf="isPbSchema(fe)" [pbSchema]=fe (radio)=onRadioClick($event) + (validChange)=onElementValid() (inputChange)=onInputChange($event)> + </pb-schema> + + <div fxHide.sm fxFlex.gt-sm="0 0 16px"></div> + </div> + + </ng-template> + </div> <mat-card-actions> <!-- bouton calculer --> diff --git a/src/app/components/generic-calculator/calculator.component.scss b/src/app/components/generic-calculator/calculator.component.scss index 1aa9e9e26cef4abd09fc8f0a9594811c82e64d36..14be9aa77c1dc1aafdb15e3230eb4e8a40cb2801 100644 --- a/src/app/components/generic-calculator/calculator.component.scss +++ b/src/app/components/generic-calculator/calculator.component.scss @@ -26,6 +26,10 @@ margin-bottom: 1em; } +#pb-schema-container { + display: block; +} + mat-card { margin-bottom: 2em; diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts index b662a5000d25250ee124089a9a93d715453b9ad5..a377bd9398cda1ff9239ba8a06b6664db2774673 100644 --- a/src/app/components/generic-calculator/calculator.component.ts +++ b/src/app/components/generic-calculator/calculator.component.ts @@ -47,6 +47,7 @@ import { PabTable } from "../../formulaire/elements/pab-table"; import { MultiDimensionResults } from "../../results/multidimension-results"; import { NgParameter } from "../../formulaire/elements/ngparam"; import { FormulaireFixedVar } from "../../formulaire/definition/form-fixedvar"; +import { PbSchema } from "../../formulaire/elements/pb-schema"; import { HotkeysService, Hotkey } from "angular2-hotkeys"; @@ -168,24 +169,26 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe /** * détermine si un FormulaireElement est du type FieldSet */ + /** détermine si un FormulaireElement est du type FieldSet */ public isFieldset(fe: any): boolean { return fe instanceof FieldSet; } - /** - * détermine si un FormulaireElement est du type FieldsetContainer - */ + /** détermine si un FormulaireElement est du type FieldsetContainer */ public isFieldsetContainer(fe: any): boolean { return fe instanceof FieldsetContainer; } - /** - * détermine si un FormulaireElement est du type PabTable - */ + /** détermine si un FormulaireElement est du type PabTable */ public isPabTable(fe: any): boolean { return fe instanceof PabTable; } + /** détermine si un FormulaireElement est du type PbSchema */ + public isPbSchema(fe: any): boolean { + return fe instanceof PbSchema; + } + public get hasForm() { return this._formulaire !== undefined; } @@ -583,7 +586,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe // for "one wide column" layout public get isWide() { - return (this.isPAB || this.isMRC); + return (this.isPAB || this.isMRC || this.isPB); } // true if current Nub is Solveur @@ -601,6 +604,11 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe return this.is(CalculatorType.MacroRugoCompound); } + // true if current Nub is PreBarrage + public get isPB() { + return this.is(CalculatorType.PreBarrage); + } + // true if current Nub is Jet public get isJet() { return this.is(CalculatorType.Jet); diff --git a/src/app/components/pb-schema/pb-schema.component.html b/src/app/components/pb-schema/pb-schema.component.html new file mode 100644 index 0000000000000000000000000000000000000000..80b06a0f1eb7473f42e734488f4fd78a6cde1992 --- /dev/null +++ b/src/app/components/pb-schema/pb-schema.component.html @@ -0,0 +1,62 @@ +<mat-card-header class="mat-card-header-text-margin-0 bg-accent-light"> + <mat-card-title> + {{ title }} + </mat-card-title> +</mat-card-header> + +<mat-card-content> + + <div id="pb-schema-toolbar"> + + <div class="hyd-window-btns"> + <span class="related-entity-title"> + {{ prefixedItemDescription }} + </span> + <!-- <mat-select id="add-many-children" [(value)]="childrenToAdd"> + <mat-option *ngFor="let i of addManyOptionsList" [value]="i"> + {{ i }} + </mat-option> + </mat-select> --> + <button type="button" mat-icon-button color="primary" title="example button"> + <mat-icon>add_box</mat-icon> + </button> + <!-- <button type="button" mat-icon-button color="primary" [disabled]="! enableAddButton" (click)="onAddClick()" + [title]="uitextAdd"> + <mat-icon>add_box</mat-icon> + </button> + <button type="button" mat-icon-button color="primary" [disabled]="! enableCopyButton" (click)="onCopyClick()" + [title]="uitextCopy"> + <mat-icon>content_copy</mat-icon> + </button> + | + <button type="button" mat-icon-button color="primary" [disabled]="! enableRemoveButton" (click)="onRemoveClick()" + [title]="uitextRemove"> + <mat-icon>delete</mat-icon> + </button> + <button type="button" mat-icon-button color="primary" [disabled]="! enableUpButton" (click)="onMoveUpClick()" + [title]="uitextMoveUp"> + <mat-icon *ngIf="! selectionIsOneDevice">arrow_upward</mat-icon> + <mat-icon *ngIf="selectionIsOneDevice">arrow_back</mat-icon> + </button> + <button type="button" mat-icon-button color="primary" [disabled]="! enableDownButton" (click)="onMoveDownClick()" + [title]="uitextMoveDown"> + <mat-icon *ngIf="! selectionIsOneDevice">arrow_downward</mat-icon> + <mat-icon *ngIf="selectionIsOneDevice">arrow_forward</mat-icon> + </button> + | + <button type="button" mat-icon-button color="primary" (click)="exportAsSpreadsheet()" + [title]="uitextExportAsSpreadsheet"> + <mat-icon color="primary">file_download</mat-icon> + </button> --> + </div> + </div> + + <div *ngIf="error">{{ uitextDrawingError }}</div> + + <div id="schema" #schema></div> + + <div *ngIf="showDebug"> + <pre>{{ graphDef }}</pre> + </div> + +</mat-card-content> diff --git a/src/app/components/pb-schema/pb-schema.component.scss b/src/app/components/pb-schema/pb-schema.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..1b30459a2a99cf49a58b888f5615fc20dcb6ca0d --- /dev/null +++ b/src/app/components/pb-schema/pb-schema.component.scss @@ -0,0 +1,58 @@ +/** @see additional styles in src/styles.css */ + +:host { + display: block; + width: 100%; + // reduce margins to avoid inner field-sets being too narrow on 360px display + /* margin-left: -8px; + margin-right: -8px; */ +} + +mat-card-header { + /* margin-left: -8px; + margin-right: -8px; */ + margin-left: -16px; + margin-right: -16px; + padding-left: 16px; + padding-top: 8px; + color: white; + + // Pourquoi n'est-ce pas hérité de calculator.component.scss ? + // À cause de la surcharge de mat-card-header ci-dessus ? + mat-card-title { + font-size: 16px !important; + margin-bottom: 8px; + } +} + +mat-card-content { + margin-top: 1em; +} + +#pb-schema-toolbar { + #edit-pab-table { + float: left; + } + .related-entity-title { + vertical-align: middle; + font-weight: bold; + } + .hyd-window-btns { + text-align: right; + + #add-many-children { + width: 3em; + vertical-align: middle; + } + + button.mat-icon-button { + width: 32px; + } + } +} + +#schema { + margin-top: .5em; + margin-bottom: .5em; + text-align: center; +} diff --git a/src/app/components/pb-schema/pb-schema.component.ts b/src/app/components/pb-schema/pb-schema.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..02a921050786469d7930e3431f8ea802e9595840 --- /dev/null +++ b/src/app/components/pb-schema/pb-schema.component.ts @@ -0,0 +1,399 @@ +import { Component, Input, Output, EventEmitter, OnInit, AfterViewInit, ViewChild } from "@angular/core"; + +import { + PreBarrage, PbBassin, PbBassinParams, PbCloison + } from "jalhyd"; + +import * as mermaid from "mermaid"; + +import { I18nService } from "../../services/internationalisation.service"; +import { ApplicationSetupService } from "../../services/app-setup.service"; +import { NotificationsService } from "../../services/notifications.service"; +import { PbSchema } from "../../formulaire/elements/pb-schema"; + +/** + * The interactive schema for calculator type "PreBarrage" (component) + */ +@Component({ + selector: "pb-schema", + templateUrl: "./pb-schema.component.html", + styleUrls: [ + "./pb-schema.component.scss" + ] +}) +export class PbSchemaComponent implements AfterViewInit, OnInit { + + @Input() + private pbSchema: PbSchema; + + @ViewChild("schema", { static: true }) + public schema: any; + + /** handle on SVG container */ + private nativeElement: any; + + public error: boolean; + + /** flag de validité des FieldSet enfants */ + private _isValid = false; + + private upstreamId = "amont"; + + private downstreamId = "aval"; + + /** événément de changement de validité */ + @Output() + private validChange = new EventEmitter(); + + /** événément de changement de valeur d'un input */ + @Output() + private inputChange = new EventEmitter(); + + /** underlying PB */ + private model: PreBarrage; + + /** Latest clicked item: a PbCloison, a PbBassin or undefined if river "Upstream" or "Downstream" was clicked */ + private _selectedItem: any; + + public constructor( + private i18nService: I18nService, + private appSetupService: ApplicationSetupService, + private notifService: NotificationsService + ) { } + + public get selectedItem(): any { + return this._selectedItem; + } + + public ngAfterContentInit(): void { + this.error = false; + mermaid.initialize({ + flowchart: { + curve: "basis" + } + }); + this.nativeElement = this.schema.nativeElement; + + // generate graph description + const graphDefinition = this.graphDefinition(); + // draw + try { + mermaid.render("graphDiv", graphDefinition, (svgCode, bindFunctions) => { + this.nativeElement.innerHTML = svgCode; + }); + } catch (e) { + console.error(e); + this.error = true; + } + } + + public ngAfterViewInit(): void { + this.refreshEventListeners(); + this.updateValidity(); + } + + /** Add click listener on every node and link in the graph */ + private refreshEventListeners() { + this.nativeElement.querySelectorAll("g.node").forEach(item => { + item.style.cursor = "pointer"; + item.addEventListener("click", () => { + this.selectBasin(item.id); + }); + }); + this.nativeElement.querySelectorAll("g.edgeLabel").forEach(item => { + item.style.cursor = "pointer"; + item.addEventListener("click", () => { + this.selectWall(item); + }); + }); + } + + /** + * Builds a Mermaid graph text definition + */ + private graphDefinition() { + const def: string[] = [ "graph TB" ]; + + def.push(`${this.upstreamId}("${this.i18nService.localizeText("INFO_LIB_AMONT")}")`); + def.push(`${this.downstreamId}("${this.i18nService.localizeText("INFO_LIB_AVAL")}")`); + + // debug + const b1 = new PbBassin(new PbBassinParams(0.1, 42)); + this.model.addChild(b1); + const b2 = new PbBassin(new PbBassinParams(0.15, 38)); + this.model.addChild(b2); + this.model.addChild(new PbCloison(undefined, b1)); + this.model.addChild(new PbCloison(b1, b2)); + this.model.addChild(new PbCloison(b2, undefined)); + this.model.addChild(new PbCloison(b1, undefined)); + + for (const b of this.model.bassins) { + // basin + def.push(`${b.uid}("${this.itemDesription(b)}")`); + // upstream walls + for (const uw of b.cloisonsAmont) { + const upstreamBasinId = uw.bassinAmont === undefined ? this.upstreamId : uw.bassinAmont.uid; + // upstream wall unique identifier + const uwString = `${upstreamBasinId}-->|${this.itemDesription(uw)}|${b.uid}`; + if (! def.includes(uwString)) { + def.push(uwString); + } + } + // downstream walls + for (const dw of b.cloisonsAval) { + const downstreamBasinId = dw.bassinAval === undefined ? this.downstreamId : dw.bassinAval.uid; + // downstream wall unique identifier + const dwString = `${b.uid}-->|${this.itemDesription(dw)}|${downstreamBasinId}`; + if (! def.includes(dwString)) { + def.push(dwString); + } + } + } + + return def.join("\n"); + } + + private selectBasin(id: string) { + if ([ this.upstreamId, this.downstreamId ].includes(id)) { + console.log("YOU CLICKED EITHER UPSTREAM OR DOWNSTREAM"); + this._selectedItem = undefined; + } else { + let basin: PbBassin; + for (const b of this.model.bassins) { + if (b.uid === id) { + basin = b; + } + } + this._selectedItem = basin; + // @TODO highlight node in schema + console.log("BASIN FOUND !", basin); + } + } + + private selectWall(item: SVGGElement) { + // Mermaid does not allow to assign IDs to connectors and labels… + const text: string = item.querySelector("span.edgeLabel").textContent; + if (text) { + const [ uBs, dBs ] = text.split("-"); + let wall: PbCloison; + // clodo test: is there an upstream basin or is it upstream river ? + if (uBs === this.i18nService.localizeText("INFO_LIB_AMONT")) { + // find wall from downstream basin + const dBi = Number(dBs.substring(1)); + const dB = this.model.bassins[dBi - 1]; + for (const w of dB.cloisonsAmont) { + // find the one that is connected to upstream river + if (w.bassinAmont === undefined) { + wall = w; + } + } + } else { + // find wall from upstream basin + const uBi = Number(uBs.substring(1)); + const uB = this.model.bassins[uBi - 1]; + // clodo test again + let dB: PbBassin; + if (dBs !== this.i18nService.localizeText("INFO_LIB_AVAL")) { + const dBi = Number(dBs.substring(1)); + dB = this.model.bassins[dBi - 1]; + } + for (const w of uB.cloisonsAval) { + // find the one that is connected to dB (either a basin or downstream river) + if (w.bassinAval === dB) { + wall = w; + } + } + } + if (wall === undefined) { + throw new Error(`PbSchemaComponent.selectWall(): cannot find wall for label "${text}"`); + } + this._selectedItem = wall; + // @TODO highlight label and edge in schema + console.log("WALL FOUND !", wall); + } + } + + public get graphDef(): string { + return this.graphDefinition(); + } + + public get title(): string { + return this.i18nService.localizeText("INFO_PB_SCHEMA"); + } + + /** Global Pb validity */ + public get isValid() { + return this._isValid; + } + + /** + * Checks that input value is a valid number, according to input[type="number"] algorithm, + * and stores it in cell.uiValidity, so that the <td> element can access it and get angry + * if input is invalid + */ + public inputValueChanged($event, cell) { + if ($event && $event.target && $event.target.validity) { + cell.uiValidity = $event.target.validity.valid; + } + this.updateValidity(); + // send input change event (used to reset form results) + this.inputChange.emit(); + } + + public get prefixedItemDescription(): string { + let desc = this.itemDesription(this._selectedItem); + if (this._selectedItem instanceof PbCloison) { + desc = this.i18nService.localizeText("INFO_PB_CLOISON") + " " + desc; + } + if (desc !== "") { + desc += " : "; + } + return desc; + } + + /** Returns a short description of the given item: wall or basin */ + private itemDesription(item: PbCloison | PbBassin): string { + let desc = ""; + if (item instanceof PbCloison) { + const upstreamBasinName = item.bassinAmont === undefined + ? this.i18nService.localizeText("INFO_LIB_AMONT") + : "B" + (item.bassinAmont.findPositionInParent() + 1); + const downstreamBasinName = item.bassinAval === undefined + ? this.i18nService.localizeText("INFO_LIB_AVAL") + : "B" + (item.bassinAval.findPositionInParent() + 1); + desc = upstreamBasinName + "-" + downstreamBasinName; + + } else if (item instanceof PbBassin) { + desc = this.i18nService.localizeText("INFO_PB_BASSIN_N") + (item.findPositionInParent() + 1); + } // else undefined + return desc; + } + + /** + * Returns true if current cell is bound to a model that says its input value is + * no valid, or if characters typed in the input field are not a valid number + * (read from cell.uiValidity, see inputValueChanged() above) + */ + /* public isInvalid(cell: any): boolean { + let valid = true; + if (this.hasModel(cell) && cell.model instanceof ParamDefinition) { + valid = valid && cell.model.isValid; + } + if (cell.uiValidity !== undefined) { + valid = valid && cell.uiValidity; + } + return ! valid; + } */ + + /** + * returns true if every wall (including downwall) has its nth device + * selected (or has no nth device) + */ + /* public isDeviceColumnSelected(n: number): boolean { + let ok = true; + for (const c of this.model.children) { + const nthChild = c.getChildren()[n]; + if (nthChild) { + ok = ok && this.selectedItems.includes(nthChild); + } + } + const nthChildDW = this.model.downWall.getChildren()[n]; + if (nthChildDW) { + ok = ok && this.selectedItems.includes(nthChildDW); + } + return ok; + } */ + + // quick getter for 1st selected item + /* public get selectedItem() { + if (this.selectedItems.length === 0) { + throw new Error("get selectedItem() : no item selected"); + } + return this.selectedItems[0]; + } */ + + // at this time @Input data is supposed to be already populated + public ngOnInit() { + this.model = this.pbSchema.pb; + this.refresh(); + } + + /** Unselects all selected text (side-effect of shift+clicking) */ + /* private clearSelection() { + if (window.getSelection) { + const sel = window.getSelection(); + sel.removeAllRanges(); + } + } */ + + /** + * Builds the interactive schema from the PreBarrage model + */ + private refresh() { + this.updateValidity(); + } + + /* public get relatedEntityTitle() { + let title = ""; + if (this.onlyDevicesAreSelected()) { + title = this.i18nService.localizeText("INFO_PAB_OUVRAGES"); + } else if (this.onlyWallsAreSelected()) { + title = this.i18nService.localizeText("INFO_PAB_BASSINS"); + } + if (title !== "") { + title += " :"; + } + return title; + } + + public get enableAddButton() { + return ( + this.onlyDevicesOfTheSameColumnAreSelected() + || ( + this.selectedItems.length === 1 + && ! (this.selectedItem instanceof CloisonAval) // exclude downwall + ) + ); + } + + public get enableCopyButton() { + return this.enableAddButton; + } */ + + /** + * Computes the global Pab validity : validity of every cell of every row + */ + private updateValidity() { + this._isValid = true; + /* for (const r of this.rows) { + for (const c of r.cells) { + this._isValid = this._isValid && ! this.isInvalid(c); + } + } */ + this.validChange.emit(); + } + + /* public exportAsSpreadsheet() { + const elem: any = document.getElementById("geometry"); + const elemCopy = (elem as HTMLElement).cloneNode(true) as HTMLElement; + // enrich element copy: replace inputs by their values, so that it appears in the exported spreadsheet + const tables: any = elemCopy.getElementsByTagName("table"); + for (const table of tables) { + const tds: any = table.getElementsByTagName("td"); + for (const td of tds) { + // if it contains an input, replace it with the input value + const inputs = td.getElementsByTagName("input"); + if (inputs.length > 0) { + const input = inputs[0]; + td.innerHTML = input.value; + } + } + } + // export the enriched element copy + AppComponent.exportAsSpreadsheet(elemCopy as any); + } + + public get uitextExportAsSpreadsheet() { + return this.i18nService.localizeText("INFO_RESULTS_EXPORT_AS_SPREADSHEET"); + } */ +} diff --git a/src/app/config.json b/src/app/config.json index 35925d7698c2eea9e7e93e31e6f9e324a494d549..6a7b7f921207b6ba1f7cf81536dd08da0019546e 100644 --- a/src/app/config.json +++ b/src/app/config.json @@ -15,7 +15,7 @@ "path": "passe-bassin.jpg", "credits": "S. Richard / OFB" }, - "calculators": [ 12, 13, 6, 5, 10, 15 ] + "calculators": [ 12, 13, 6, 5, 10, 15, 30 ] }, { "name": "PASSE_A_RALENTISSEURS", diff --git a/src/app/formulaire/definition/form-definition.ts b/src/app/formulaire/definition/form-definition.ts index 2baaff2cf28ad4c9916a9243d5cbb9430a60d04d..707f0268c75e2f042a1f0378115558b4d855f00a 100644 --- a/src/app/formulaire/definition/form-definition.ts +++ b/src/app/formulaire/definition/form-definition.ts @@ -24,6 +24,7 @@ import { CalculatorResults } from "../../results/calculator-results"; import { ServiceFactory } from "../../services/service-factory"; import { PabTable } from "../elements/pab-table"; import { SelectEntry } from "../elements/select-entry"; +import { PbSchema } from '../elements/pb-schema'; /** * classe de base pour tous les formulaires @@ -217,6 +218,13 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs this.kids.push(tab); } + private parse_pb_schema(json: {}) { + const sch: PbSchema = new PbSchema(this); + sch.parseConfig(json); + this.kids.push(sch); + } + + /** * 1ère passe d'analyse de la configuration */ @@ -274,6 +282,10 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs this.parse_pab_table(conf); break; + case "pb_schema": // not generic at all + this.parse_pb_schema(conf); + break; + default: throw new Error(`type d'objet de module de calcul ${type} non pris en charge`); } diff --git a/src/app/formulaire/elements/pab-table.ts b/src/app/formulaire/elements/pab-table.ts index 3601cf19b5ee2e7a4e6569ce6f6b2551e9122ed8..e9934bb9c5996e828f4c887d3bdb5de86de16ada 100644 --- a/src/app/formulaire/elements/pab-table.ts +++ b/src/app/formulaire/elements/pab-table.ts @@ -1,7 +1,6 @@ import { Pab } from "jalhyd"; import { FormulaireElement } from "./formulaire-element"; -import { FormulaireNode } from "./formulaire-node"; import { FormulairePab } from "../definition/form-pab"; /** diff --git a/src/app/formulaire/elements/pb-schema.ts b/src/app/formulaire/elements/pb-schema.ts new file mode 100644 index 0000000000000000000000000000000000000000..0d424378155a0a2977d501eea2a5282916c709c9 --- /dev/null +++ b/src/app/formulaire/elements/pb-schema.ts @@ -0,0 +1,32 @@ +import { PreBarrage } from "jalhyd"; + +import { FormulaireElement } from "./formulaire-element"; + +/** + * The interactive schema for calculator type "PreBarrage" (form element). + * + * This is just a gateway between the model (Prebarrage) + * and the user interface (PbSchemaComponent) + */ +export class PbSchema extends FormulaireElement { + + public parseConfig(json: {}) { + this._confId = json["id"]; + } + + /** + * Returns the parent FormulairePab + */ + /* public get form(): FormulairePab { + return this.parentForm as FormulairePab; + } */ + + /** + * Returns the Prebarrage model associated to the parent form + */ + public get pb(): PreBarrage { + if (this.parentForm) { + return this.parentForm.currentNub as PreBarrage; + } + } +} diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json index 8f48e654cd1f0835c9eea076b83006dc174d4745..09b6c4cfee0def998aac0f61c41ee7a0d781f515 100644 --- a/src/locale/messages.en.json +++ b/src/locale/messages.en.json @@ -516,6 +516,11 @@ "INFO_PARAMFIELD_VARIATED": "Variated", "INFO_PARAMMODE_LIST": "Values list", "INFO_PARAMMODE_MINMAX": "Min/max", + "INFO_PB_BASSIN_N": "Basin #", + "INFO_PB_CLOISON": "Wall", + "INFO_PB_SCHEMA": "Basins layout", + "INFO_PREBARRAGE_TITRE": "Pre-dams", + "INFO_PREBARRAGE_TITRE_COURT": "Pre-dams", "INFO_QUICKNAV_CHARTS": "charts", "INFO_QUICKNAV_INPUT": "input", "INFO_QUICKNAV_RESULTS": "results", diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json index 6ce7511418153d363b0434466027aead75ac003b..8e418c3817598e549e360962f1eaa0accaf20703 100644 --- a/src/locale/messages.fr.json +++ b/src/locale/messages.fr.json @@ -517,6 +517,11 @@ "INFO_PARAMFIELD_VARIATED": "Varié", "INFO_PARAMMODE_LIST": "Liste de valeurs", "INFO_PARAMMODE_MINMAX": "Min/max", + "INFO_PB_BASSIN_N": "Bassin n°", + "INFO_PB_CLOISON": "Cloison", + "INFO_PB_SCHEMA": "Organisation des bassins", + "INFO_PREBARRAGE_TITRE": "Prébarrages", + "INFO_PREBARRAGE_TITRE_COURT": "Prébarrages", "INFO_QUICKNAV_CHARTS": "graphiques", "INFO_QUICKNAV_INPUT": "données", "INFO_QUICKNAV_RESULTS": "résultats",