diff --git a/angular.json b/angular.json index 1bdcb122c0ed8d56b14fee48d3f60274ba399cde..cb7ab0bee214dd7bc0fa55fc47dc9aa3df74b162 100644 --- a/angular.json +++ b/angular.json @@ -25,8 +25,6 @@ { "glob": "**/*.png", "input": "src/", "output": "/" } ], "styles": [ - "node_modules/angular-bootstrap-md/scss/bootstrap/bootstrap.scss", - "node_modules/angular-bootstrap-md/scss/mdb-free.scss", "src/styles.scss", "src/theme.scss" ], @@ -42,7 +40,7 @@ "aot": true, "extractLicenses": true, "vendorChunk": false, - "buildOptimizer": true, + "buildOptimizer": false, "fileReplacements": [ { "replace": "src/environments/environment.ts", @@ -79,8 +77,6 @@ "tsConfig": "src/tsconfig.spec.json", "scripts": [], "styles": [ - "node_modules/angular-bootstrap-md/scss/bootstrap/bootstrap.scss", - "node_modules/angular-bootstrap-md/scss/mdb-free.scss", "src/styles.scss", "src/theme.scss" ], diff --git a/package-lock.json b/package-lock.json index f8fa418718ae8400a1092e9aa7896fb1912de27e..e5839dd8addd49453ffe173ae960ebd23db062b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1481,11 +1481,6 @@ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true }, - "angular-bootstrap-md": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/angular-bootstrap-md/-/angular-bootstrap-md-7.3.0.tgz", - "integrity": "sha512-FACXdj+fGe7aA1yNBoFFV6I8Gs9+ithMdGAl4ZJ7DxqD5JudtWqlAwapNpqXzf7r17b9+vIGAAMmVTfcc+i2Dw==" - }, "angular2-chartjs": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/angular2-chartjs/-/angular2-chartjs-0.5.1.tgz", @@ -6782,8 +6777,12 @@ "version": "file:../jalhyd", ======= "version": "file:../jalhyd/jalhyd-1.0.0.tgz", +<<<<<<< e94eb7b6543d2a58a90ae771c869e558f09f5cf5 "integrity": "sha512-sT+DU/Uyg2s04TqEHCoM8dKQDvFwjj30ax8MO2EOHa2AOzd9+OHO0z5KbvwAHYadrW4p/kxz8p2SK/KLDsfQ0g==", >>>>>>> Mà j dépendances +======= + "integrity": "sha512-aB8rBtIjyt5EKGXOWRCarFduWhLpct4gEzIHwxeU2BTQpo043CybEDN7kC2w6NprK/NSfcgZBDC9APdcjnBsig==", +>>>>>>> [WIP] materialification des modules de calcul "requires": { "buffer": "^5.2.1" }, diff --git a/package.json b/package.json index 3f6f702194ff3a3ad41ea74ef60779db70541b0f..9d2e430d150f2bc558292d5eaf4b43daed41b19a 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "@angular/platform-browser": "^7.2.1", "@angular/platform-browser-dynamic": "^7.2.1", "@angular/router": "^7.2.1", - "angular-bootstrap-md": "^7.3.0", "angular2-chartjs": "^0.5.1", "core-js": "^2.6.3", "file-saver": "^2.0.0", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 974987c227ad34fe1e042f34cf5471d2f3ea1b4e..f4473c1a06f2ea991392d3fae37c283af037642d 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -16,12 +16,19 @@ import { MatInputModule, MatListModule, MatCardModule, - ErrorStateMatcher + ErrorStateMatcher, + MatButtonToggleModule } from "@angular/material"; import { MaterialFileInputModule } from "ngx-material-file-input"; import { FlexLayoutModule } from "@angular/flex-layout"; -import { MDBBootstrapModule } from "angular-bootstrap-md"; +import { + CustomBreakPointsProvider, + FlexGtXxsShowHideDirective, + FlexXxsShowHideDirective, + FlexLtXsShowHideDirective +} from "./directives/flex-xxs.directive"; + import { HttpClientModule } from "@angular/common/http"; import { FormsModule, ReactiveFormsModule } from "@angular/forms"; // <-- NgModel lives here import { ChartModule } from "angular2-chartjs"; @@ -39,11 +46,7 @@ import { NgParamInputComponent } from "./components/ngparam-input/ngparam-input. import { FieldSetComponent } from "./components/field-set/field-set.component"; import { FieldsetContainerComponent } from "./components/fieldset-container/fieldset-container.component"; import { ParamFieldLineComponent } from "./components/param-field-line/param-field-line.component"; -import { NgParamMinComponent } from "./components/param-values/ngparam-min.component"; -import { NgParamMaxComponent } from "./components/param-values/ngparam-max.component"; -import { NgParamStepComponent } from "./components/param-values/ngparam-step.component"; import { ParamValuesComponent } from "./components/param-values/param-values.component"; -import { ValueListComponent } from "./components/param-values/value-list.component"; import { SelectFieldLineComponent } from "./components/select-field-line/select-field-line.component"; import { CheckFieldLineComponent } from "./components/check-field-line/check-field-line.component"; import { CalculatorResultsComponent } from "./components/calculator-results/calculator-results.component"; @@ -70,12 +73,15 @@ import { ParamLinkComponent } from "./components/param-link/param-link.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"; +import { DialogEditParamComputedComponent } from "./components/dialog-edit-param-computed/dialog-edit-param-computed.component"; +import { DialogEditParamValuesComponent } from "./components/dialog-edit-param-values/dialog-edit-param-values.component"; import { DialogLoadSessionComponent } from "./components/dialog-load-session/dialog-load-session.component"; import { DialogSaveSessionComponent } from "./components/dialog-save-session/dialog-save-session.component"; import { JalhydAsyncModelValidationDirective } from "./directives/jalhyd-async-model-validation.directive"; import { JalhydModelValidationDirective } from "./directives/jalhyd-model-validation.directive"; import { ImmediateErrorStateMatcher } from "./formulaire/immediate-error-state-matcher"; +import { ParamComputedComponent } from "./components/param-computed/param-computed.component"; const appRoutes: Routes = [ { path: "list", component: CalculatorListComponent }, @@ -94,6 +100,7 @@ const appRoutes: Routes = [ HttpClientModule, FlexLayoutModule, MatButtonModule, + MatButtonToggleModule, MatCardModule, MatCheckboxModule, MatDialogModule, @@ -107,7 +114,6 @@ const appRoutes: Routes = [ MatSidenavModule, MatTabsModule, MatToolbarModule, - MDBBootstrapModule.forRoot(), NgxMdModule.forRoot(), RouterModule.forRoot( appRoutes, @@ -128,12 +134,17 @@ const appRoutes: Routes = [ CheckFieldLineComponent, DialogConfirmCloseCalcComponent, DialogConfirmEmptySessionComponent, + DialogEditParamComputedComponent, + DialogEditParamValuesComponent, DialogLoadSessionComponent, DialogSaveSessionComponent, FieldSetComponent, FieldsetContainerComponent, FixedResultsComponent, FixedVarResultsComponent, + FlexGtXxsShowHideDirective, + FlexLtXsShowHideDirective, + FlexXxsShowHideDirective, GenericCalculatorComponent, GraphTypeSelectComponent, HorizontalResultElementComponent, @@ -142,9 +153,7 @@ const appRoutes: Routes = [ LogComponent, LogEntryComponent, NgParamInputComponent, - NgParamMaxComponent, - NgParamMinComponent, - NgParamStepComponent, + ParamComputedComponent, ParamFieldLineComponent, ParamLinkComponent, ParamValuesComponent, @@ -154,18 +163,20 @@ const appRoutes: Routes = [ SectionCanvasComponent, SectionResultsComponent, SelectFieldLineComponent, - ValueListComponent, VarResultsComponent, VerticalResultElementComponent ], entryComponents: [ DialogConfirmCloseCalcComponent, DialogConfirmEmptySessionComponent, + DialogEditParamComputedComponent, + DialogEditParamValuesComponent, DialogSaveSessionComponent, DialogLoadSessionComponent ], providers: [ // services ApplicationSetupService, + CustomBreakPointsProvider, FormulaireService, HttpService, I18nService, diff --git a/src/app/components/base-param-input/base-param-input.component.ts b/src/app/components/base-param-input/base-param-input.component.ts index a919e31a7c75ab8750a93f8a96b9c0dfc19c5d72..fb65a845cc5c4c2d59a99968641c67d9e9fb5486 100644 --- a/src/app/components/base-param-input/base-param-input.component.ts +++ b/src/app/components/base-param-input/base-param-input.component.ts @@ -112,25 +112,4 @@ export class BaseParamInputComponent extends GenericInputComponent { protected validateModelValue(v: any): { isValid: boolean, message: string } { return this._model.validateModelValue(v); } - - protected modelToUI(v: any): string { - return String(v); - } - - protected validateUIValue(ui: string): { isValid: boolean, message: string } { - let valid = false; - let msg: string; - - if (! isNumeric(ui)) { - msg = "Veuillez entrer une valeur numérique"; - } else { - valid = true; - } - - return { isValid: valid, message: msg }; - } - - protected uiToModel(ui: string) { - return +ui; - } } diff --git a/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.html b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.html new file mode 100644 index 0000000000000000000000000000000000000000..9efaf288795c96ec273dc07655f1f13889cfad2a --- /dev/null +++ b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.html @@ -0,0 +1,16 @@ +<h1 mat-dialog-title [innerHTML]="uitextEditParamComputedInitialValue"></h1> + +<form> + + <div mat-dialog-content> + <ngparam-input [title]="param.title" ></ngparam-input> + <!-- (change)="onInputChange($event)" --> + </div> + + <div mat-dialog-actions> + <button mat-raised-button [mat-dialog-close]="true" cdkFocusInitial> + {{ uitextClose }} + </button> + </div> + +</form> diff --git a/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.scss b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..bc76a27e25280cc38af4ab2f9eb032683c35e758 --- /dev/null +++ b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.scss @@ -0,0 +1,3 @@ +mat-form-field { + width: 100%; +} diff --git a/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.ts b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..e7979965fce7f80717be0d9086fddc818922c6e1 --- /dev/null +++ b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.ts @@ -0,0 +1,40 @@ +import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material"; +import { Inject, Component, ViewChild, OnInit } from "@angular/core"; +import { I18nService } from "../../services/internationalisation/internationalisation.service"; +import { NgParameter } from "../../formulaire/ngparam"; +import { NgParamInputComponent } from "../ngparam-input/ngparam-input.component"; + +@Component({ + selector: "dialog-edit-param-computed", + templateUrl: "dialog-edit-param-computed.component.html", + styleUrls: ["dialog-edit-param-computed.component.scss"] +}) +export class DialogEditParamComputedComponent implements OnInit { + + /** the related parameter to change the "fixed" value of */ + public param: NgParameter; + + @ViewChild(NgParamInputComponent) + private _ngParamInputComponent: NgParamInputComponent; + + constructor( + public dialogRef: MatDialogRef<DialogEditParamComputedComponent>, + private intlService: I18nService, + @Inject(MAT_DIALOG_DATA) public data: any + ) { + this.param = data.param; + } + + public get uitextClose() { + return this.intlService.localizeText("INFO_OPTION_CLOSE"); + } + + public get uitextEditParamComputedInitialValue() { + return "Modifier la valeur initiale (à traduire)"; + // return this.intlService.localizeText("INFO_OPTION_ALL"); + } + + public ngOnInit() { + this._ngParamInputComponent.model = this.param; + } +} diff --git a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html new file mode 100644 index 0000000000000000000000000000000000000000..61ae3b59b26228ab9f65690ea52dea034bfc38ae --- /dev/null +++ b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html @@ -0,0 +1,84 @@ +<h1 mat-dialog-title [innerHTML]="uitextEditParamComputedInitialValue"></h1> + + <div mat-dialog-content> + + <mat-form-field> + <mat-select [placeholder]="uiTextModeSelection" [(value)]="selectedValueMode" (selectionChange)="onValueModeChange($event)" + data-testid="variable-value-mode-select"> + <mat-option *ngFor="let e of valueModes" [value]="e.value"> + {{ e.label }} + </mat-option> + </mat-select> + </mat-form-field> + + <div *ngIf="isMinMax" class="min-max-step-container"> + <form> + <mat-form-field> + <input matInput class="form-control" type="number" inputmode="numeric" name="min-value" min="0" step="0.01" + [placeholder]="uitextValeurMini" [(ngModel)]="param.minValue" required> + + <mat-error>{{ errorMessage }}</mat-error> + </mat-form-field> + + <mat-form-field> + <input matInput class="form-control" type="number" inputmode="numeric" name="max-value" min="0" step="0.01" + [placeholder]="uitextValeurMaxi" [(ngModel)]="param.maxValue" required> + + <mat-error>{{ errorMessage }}</mat-error> + </mat-form-field> + + <mat-form-field> + <input matInput class="form-control" type="number" inputmode="numeric" name="step-value" min="0" step="0.01" + [placeholder]="uitextPasVariation" [(ngModel)]="param.stepValue" required> + + <mat-error>{{ errorMessage }}</mat-error> + </mat-form-field> + </form> + </div> + + <div *ngIf="isListe"> + <form [formGroup]="valuesListForm"> + <mat-form-field> + <textarea matInput matTextareaAutosize [placeholder]="uitextListeValeurs" [ngModel]="valuesList" + name="values-list" #vl="ngModel" required [pattern]="valuesListPattern"></textarea> + <mat-error *ngIf="vl.errors"> + <div *ngIf="vl.errors.required || vl.errors.pattern"> + {{ uitextMustBeListOfNumbers }} + </div> + <!-- <div *ngIf="! vl.errors.required && vl.errors.jalhydModel"> + {{ vl.errors.jalhydModel.message }} + </div> --> + </mat-error> + </mat-form-field> + + <div class="decimal-separator-and-file-container" fxLayout="row wrap" fxLayoutAlign="space-between start"> + <mat-form-field class="decimal-separator" fxFlex.gt-xs="1 0 auto" fxFlex.lt-sm="1 0 100%"> + <mat-select [placeholder]="uitextDecimalSeparator" [(value)]="decimalSeparator" + data-testid="decimal-separator-select"> + <mat-option *ngFor="let e of decimalSeparators" [value]="e.value"> + {{ e.label }} + </mat-option> + </mat-select> + </mat-form-field> + + <div fxHide.xs fxFlex.gt-xs="0 0 16px"></div> + + <mat-form-field class="values-file" fxFlex.gt-xs="1 0 auto" fxFlex.lt-sm="1 0 100%"> + <ngx-mat-file-input #valuesFile [placeholder]="uitextImportFile" + (change)="onFileSelected($event)" formControlName="file"> + </ngx-mat-file-input> + <button mat-icon-button matSuffix *ngIf="!valuesFile.empty" (click)="valuesFile.clear($event)"> + <mat-icon>clear</mat-icon> + </button> + </mat-form-field> + </div> + </form> + </div> + + </div> + + <div mat-dialog-actions> + <button mat-raised-button [mat-dialog-close]="true" cdkFocusInitial> + {{ uitextClose }} + </button> + </div> diff --git a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.scss b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..c411650ebcff70179e2e847bbff3d8a0aa08636d --- /dev/null +++ b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.scss @@ -0,0 +1,16 @@ +mat-form-field { + display: block; + + textarea { + font-size: .8em; + max-height: 100px; + } + + /*&.decimal-separator, &.values-file { + display: inline-block; + }*/ +} + +.min-max-step-container { + margin-top: -8px; +} diff --git a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..76b32a8771b409e823883c93cb96d5b0318dff7d --- /dev/null +++ b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts @@ -0,0 +1,271 @@ +import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material"; +import { Inject, Component, OnInit } from "@angular/core"; +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; +import { I18nService } from "../../services/internationalisation/internationalisation.service"; +import { NgParameter } from "../../formulaire/ngparam"; +import { ParamValueMode } from "jalhyd"; + +@Component({ + selector: "dialog-edit-param-values", + templateUrl: "dialog-edit-param-values.component.html", + styleUrls: ["dialog-edit-param-values.component.scss"] +}) +export class DialogEditParamValuesComponent implements OnInit { + + /** the related parameter to change the "variable" value of */ + public param: NgParameter; + + /** available value modes (min / max, list) */ + public valueModes; + + /** available decimal separators */ + public decimalSeparators; + + /** current decimal separator */ + public decimalSeparator; + + public valuesListForm: FormGroup; + + constructor( + public dialogRef: MatDialogRef<DialogEditParamValuesComponent>, + private intlService: I18nService, + private fb: FormBuilder, + @Inject(MAT_DIALOG_DATA) public data: any + ) { + this.param = data.param; + if (this.isMinMax) { + console.log("PARAM MIN VALUE", this.param.minValue); + console.log("PARAM MAX VALUE", this.param.maxValue); + console.log("PARAM STEP VALUE", this.param.stepValue); + } + if (this.isListe) { + console.log("PARAM LIST VALUE", this.param.valueList); + } + + // an explicit ReactiveForm is required for file input component + this.valuesListForm = this.fb.group({ + file: [null, null] + }); + + this.valueModes = [ + { + value: ParamValueMode.MINMAX, + label: "Min/max" + }, + { + value: ParamValueMode.LISTE, + label: "Liste (à traduire)" + } + ]; + this.decimalSeparators = [ + { + label: "Point (à traduire)", + value: "." + }, + { + label: "Virgule (à traduire)", + value: "," + } + ]; + this.decimalSeparator = this.decimalSeparators[0].value; + } + + /** regular expression pattern for values list validation (depends on decimal separator) */ + public get valuesListPattern() { + const escapedDecimalSeparator = (this.decimalSeparator === "." ? "\\." : this.decimalSeparator); + const numberSubPattern = "-?([0-9]+" + escapedDecimalSeparator + ")?[0-9E]+"; + const re = numberSubPattern + "(" + this.separatorPattern + numberSubPattern + ")*"; + // console.log("Re PAT", re); + return re; + } + + /** accepted separator: everything but [numbers, E, +, -, decimal separator], any length */ + public get separatorPattern() { + return "[^0-9-+E" + this.decimalSeparator + "]+"; + } + + public get selectedValueMode() { + return this.param.valueMode; + } + + public set selectedValueMode(v) { + this.param.valueMode = v; + } + + public get uiTextModeSelection() { + return "Mode de variabilité (à traduire)"; + } + + public get uitextValeurMini() { + return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMINI"); + } + + public get uitextValeurMaxi() { + return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMAXI"); + } + + public get uitextPasVariation() { + return this.intlService.localizeText("INFO_PARAMFIELD_PASVARIATION"); + } + + public get uitextClose() { + return this.intlService.localizeText("INFO_OPTION_CLOSE"); + } + + public get uitextEditParamComputedInitialValue() { + return "Modifier les valeurs variables (à traduire)"; + // return this.intlService.localizeText("INFO_OPTION_ALL"); + } + + public get uitextListeValeurs() { + return "valeurs séparées par ';' (à traduire)"; + } + + public get uitextMustBeListOfNumbers() { + return "les valeurs doivent être de la forme 3;4.5;6 (à traduire)"; + } + + public get uitextDecimalSeparator() { + return "Séparateur décimal (à traduire)"; + } + + public get uitextImportFile() { + return "Importer un fichier (à traduire)"; + } + + public get isMinMax() { + return this.param.valueMode === ParamValueMode.MINMAX; + } + + public get isListe() { + return this.param.valueMode === ParamValueMode.LISTE; + } + + public get valuesList() { + return (this.param.valueList || []).join(";"); + } + + public set valuesList(list: string) { + const vals = []; + list.split(";").forEach((e) => { + if (e.length > 0) { + vals.push(Number(e)); + } + }); + this.param.valueList = vals; + } + + public onFileSelected(event: any) { + if (event.target.files && event.target.files.length) { + console.log("FICHIERS", event.target.files); + const fr = new FileReader(); + fr.onload = () => { + console.log("CHARJAY", fr.result); + this.valuesList = String(fr.result); + }; + fr.onerror = () => { + fr.abort(); + throw new Error("Erreur de lecture du fichier"); + }; + fr.readAsText(event.target.files[0]); + } + } + + public dumperr(obj) { + if (obj) { return Object.keys(obj).join(", "); } + } + + public onValueModeChange(event) { + this.initVariableValues(); + } + + public ngOnInit() { + this.initVariableValues(); + } + + private initVariableValues() { + // init min / max / step + if (this.isMinMax) { + if (this.param.minValue === undefined) { + this.param.minValue = this.param.getValue() / 2; + } + if (this.param.maxValue === undefined) { + this.param.maxValue = this.param.getValue() * 2; + } + let step = this.param.stepValue; + if (step === undefined) { + step = (this.param.maxValue - this.param.minValue) / 20; + } + this.param.stepValue = step; + } + // init values list + if (this.isListe) { + if (this.param.valueList === undefined) { + if (this.param.isDefined) { + console.log("SET LIST", [ this.param.getValue() ]); + this.param.valueList = [ this.param.getValue() ]; + } else { + console.log("SET LIST", [ ]); + this.param.valueList = []; + } + } + } + } + + + /* protected validateModelValue(v: any): { isValid: boolean, message: string } { + let msg: string; + let valid = false; + + if (this.param === undefined) { + msg = "internal error, model undefined"; + } else { + if (!this.param.checkMin(v)) { + msg = "La valeur n'est pas dans [" + this.param.domain.minValue + " , " + this.param.maxValue + "["; + } else { + valid = true; + } + } + + return { isValid: valid, message: msg }; + } + protected validateModelValue(v: any): { isValid: boolean, message: string } { + let msg: string; + let valid = false; + + if (this.param === undefined) { + msg = "internal error, model undefined"; + } else { + if (!this.param.checkMax(v)) { + msg = "La valeur n'est pas dans ]" + this.param.minValue + " , " + this.param.domain.maxValue + "]"; + } else { + valid = true; + } + } + + return { isValid: valid, message: msg }; + } + protected validateModelValue(v: any): { isValid: boolean, message: string } { + let msg: string; + let valid = false; + + if (! this.param) { + msg = "internal error, model undefined"; + } else { + if (this.param.isMinMaxValid) { + if (!this.param.checkStep(v)) { + msg = "La valeur n'est pas dans " + this.param.stepRefValue.toString(); + } else { + valid = v > 0; + if (!valid) { + msg = "La valeur ne peut pas être <= 0"; + } + } + } else { + msg = "Veuillez corriger le min/max"; + } + } + + return { isValid: valid, message: msg }; + } */ +} diff --git a/src/app/components/field-set/field-set.component.html b/src/app/components/field-set/field-set.component.html index b0c0cec84f512d923cf2ad31ccea995b97a1f4cc..3217d50f0ba485c834824abf92139f58b47292d8 100644 --- a/src/app/components/field-set/field-set.component.html +++ b/src/app/components/field-set/field-set.component.html @@ -1,31 +1,31 @@ -<div class="row fieldset_backgrd"> - <div class="col fieldset_title"> +<mat-card-header class="bg-accent-light"> + <mat-card-title> {{ title }} + </mat-card-title> + <div *ngIf="showButtons" class="hyd-window-btns"> + <button mat-icon-button (click)="onAddClick()"> + <mat-icon>add_box</mat-icon> + </button> + <button mat-icon-button [disabled]="! enableRemoveButton" (click)="onRemoveClick()"> + <mat-icon>delete</mat-icon> + </button> + <button mat-icon-button [disabled]="! enableUpButton" (click)="onMoveUpClick()"> + <mat-icon>arrow_upward</mat-icon> + </button> + <button mat-icon-button [disabled]="! enableDownButton" (click)="onMoveDownClick()"> + <mat-icon>arrow_downward</mat-icon> + </button> </div> - <div *ngIf="showButtons" class="col-sm-4 hyd-window-btns"> - <mat-icon (click)='onAddClick()'>add</mat-icon> - <mat-icon [style.color]='removeButtonColor' (click)='onRemoveClick()'>delete</mat-icon> - <mat-icon [style.color]='upButtonColor' (click)='onMoveUpClick()'>arrow_upward</mat-icon> - <mat-icon [style.color]='downButtonColor' (click)='onMoveDownClick()'>arrow_downward</mat-icon> - </div> -</div> - -<!-- - <tag *ngFor="let var of array" *ngIf="...utilisation de var..." > - </tag> - peut être transformé en - <ng-template ngFor let-var [ngForOf]="array"> - <tag *ngIf="...utilisation de var..." > - </tag> - </ng-template> ---> +</mat-card-header> -<ng-template ngFor let-p [ngForOf]="fields"> - <param-field-line *ngIf="isInputField(p)" [param]=p (radio)=onRadioClick($event) (valid)=onParamLineValid() (inputChange)=onInputChange()> - </param-field-line> +<mat-card-content> + <ng-template ngFor let-p [ngForOf]="fields"> + <param-field-line *ngIf="isInputField(p)" [param]=p (radio)=onRadioClick($event) (valid)=onParamLineValid() (inputChange)=onInputChange()> + </param-field-line> - <select-field-line *ngIf="isSelectField(p)" [_select]=p> - </select-field-line> + <select-field-line *ngIf="isSelectField(p)" [_select]=p> + </select-field-line> - <check-field-line *ngIf="isCheckField(p)" [check]=p></check-field-line> -</ng-template> \ No newline at end of file + <check-field-line *ngIf="isCheckField(p)" [check]=p></check-field-line> + </ng-template> +</mat-card-content> diff --git a/src/app/components/field-set/field-set.component.scss b/src/app/components/field-set/field-set.component.scss index a4533b19585e87ad62f56339a3d820d18d863ed8..373f5b26832ff9012eb9819ced8f1b4b23fcb914 100644 --- a/src/app/components/field-set/field-set.component.scss +++ b/src/app/components/field-set/field-set.component.scss @@ -1,19 +1,84 @@ -.fieldset_title { - font-weight: bold; +param-field-line { + // border: solid green 2px; + display: block; } -.radio_param_header { - width: 10em; + +select-field-line { + // border: solid blue 2px; + display: block; +} + +mat-card-header { + 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; + margin-bottom: 8px; + } + + .hyd-window-btns { + text-align: right; + + mat-icon { + cursor: pointer; + transform: scale(1.2); + margin-top: 6px; + margin-right: 5px; + } + } } -.fieldset_backgrd { - background-color: #eeeeee; + +mat-card-content { + margin-top: 1em; } -.hyd-window-btns { - text-align: right; - - mat-icon { - cursor: pointer; - transform: scale(1.2); - margin-top: 6px; - margin-right: 5px; + +mat-card-actions { + + button { + width: 100%; + } +} + +:host { + + &.fieldset-inner { + + mat-card-header { + margin-left: 0; + margin-right: 0; + padding-left: 12px; + + mat-card-title { + font-size: 14px; + } + + .hyd-window-btns { + position: absolute; + right: 18px; + margin-top: -4px; + + button.mat-icon-button { + width: 26px; + height: 20px; + line-height: 20px; + + mat-icon { + margin-top: 0; + margin-right: 0; + transform: scale(1); + } + } + } + } + + mat-card-content { + padding: 0 1em; + } } } diff --git a/src/app/components/field-set/field-set.component.ts b/src/app/components/field-set/field-set.component.ts index 24a031d4df575d78117d2adf4c3c1e24bb8475a2..5848aa4c1f961ab145c586232f86d6717386db71 100644 --- a/src/app/components/field-set/field-set.component.ts +++ b/src/app/components/field-set/field-set.component.ts @@ -24,26 +24,6 @@ export class FieldSetComponent implements DoCheck { return this._fieldSet.kids; } - public get showButtons() { - return this._showButtons; - } - - public set showButtons(b: boolean) { - this._showButtons = b; - } - - public set enableUpButton(b: boolean) { - this._enableUpButton = b; - } - - public set enableDownButton(b: boolean) { - this._enableDownButton = b; - } - - public set enableRemoveButton(b: boolean) { - this._enableRemoveButton = b; - } - public set fieldsetNumber(n: number) { this._fieldSet.labelNumber = n; } @@ -62,26 +42,6 @@ export class FieldSetComponent implements DoCheck { return this._isValid; } - /** - * couleur du bouton monter - */ - private get upButtonColor(): string { - return this._enableUpButton ? "black" : "lightgrey"; - } - - /** - * couleur du bouton descendre - */ - private get downButtonColor(): string { - return this._enableDownButton ? "black" : "lightgrey"; - } - - /** - * couleur du bouton supprimer - */ - private get removeButtonColor(): string { - return this._enableRemoveButton ? "black" : "lightgrey"; - } /** * field set attribute */ @@ -134,22 +94,22 @@ export class FieldSetComponent implements DoCheck { /** * flag d'affichage des boutons ajouter, supprimer, monter, descendre */ - private _showButtons = false; + public showButtons = false; /** * flag d'activation du bouton monter */ - private _enableUpButton = true; + public enableUpButton = true; /** * flag d'activation du bouton descendre */ - private _enableDownButton = true; + public enableDownButton = true; /** * flag d'activation du bouton supprimer */ - private _enableRemoveButton = true; + public enableRemoveButton = true; /** * événement de changement d'état d'un radio @@ -300,26 +260,20 @@ export class FieldSetComponent implements DoCheck { * clic sur le bouton supprimer */ private onRemoveClick() { - if (this._enableRemoveButton) { - this.removeFieldset.emit(this._fieldSet); - } + this.removeFieldset.emit(this._fieldSet); } /** * clic sur le bouton monter */ private onMoveUpClick() { - if (this._enableUpButton) { - this.moveFieldsetUp.emit(this._fieldSet); - } + this.moveFieldsetUp.emit(this._fieldSet); } /** * clic sur le bouton descendre */ private onMoveDownClick() { - if (this._enableDownButton) { - this.moveFieldsetDown.emit(this._fieldSet); - } + this.moveFieldsetDown.emit(this._fieldSet); } } diff --git a/src/app/components/fieldset-container/fieldset-container.component.html b/src/app/components/fieldset-container/fieldset-container.component.html index 84e4f78e5a5c47b81372383aafb6a8126856e52f..08e323f4465f7fad1027429de458464fbd5232c8 100644 --- a/src/app/components/fieldset-container/fieldset-container.component.html +++ b/src/app/components/fieldset-container/fieldset-container.component.html @@ -1,10 +1,13 @@ -<div class="container-fluid" style="border-style:solid; border-color: lightgray; border-radius: 10px; margin-bottom: 10px;"> - <div class="row"> - <h4 class="col">{{ title }}</h4> - </div> +<mat-card-header class="bg-accent-light"> + <mat-card-title> + {{ title }} + </mat-card-title> +</mat-card-header> - <field-set *ngFor="let fs of fieldsets" [fieldSet]=fs (radio)=onRadioClick($event) (valid)=onFieldsetValid() (inputChange)=onInputChange() - (addFieldset)=onAddFieldset($event) (removeFieldset)=onRemoveFieldset($event) (moveFieldsetUp)=onMoveFieldsetUp($event) - (moveFieldsetDown)=onMoveFieldsetDown($event)> +<mat-card-content> + <field-set *ngFor="let fs of fieldsets" class="fieldset-inner" [fieldSet]=fs + (radio)=onRadioClick($event) (valid)=onFieldsetValid() (inputChange)=onInputChange() + (addFieldset)=onAddFieldset($event) (removeFieldset)=onRemoveFieldset($event) + (moveFieldsetDown)=onMoveFieldsetDown($event) (moveFieldsetUp)=onMoveFieldsetUp($event)> </field-set> -</div> \ No newline at end of file +</mat-card-content> diff --git a/src/app/components/fieldset-container/fieldset-container.component.scss b/src/app/components/fieldset-container/fieldset-container.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..bbc65715fe3bf96979687811fbac7ffc9273c3d0 --- /dev/null +++ b/src/app/components/fieldset-container/fieldset-container.component.scss @@ -0,0 +1,26 @@ +:host { + display: block; + background-color: #f0f0f0; + // 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; + 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; + margin-bottom: 8px; + } +} + +mat-card-content { + margin-top: 1em; +} diff --git a/src/app/components/fieldset-container/fieldset-container.component.ts b/src/app/components/fieldset-container/fieldset-container.component.ts index 36c8199c3098e601edbafad47f6f2bb9706e8cf7..75caa880fcfb96ab8300ad8a00a09e162b39874a 100644 --- a/src/app/components/fieldset-container/fieldset-container.component.ts +++ b/src/app/components/fieldset-container/fieldset-container.component.ts @@ -7,7 +7,10 @@ import { FormulaireDefinition } from "../../formulaire/definition/form-definitio @Component({ selector: "fieldset-container", - templateUrl: "./fieldset-container.component.html" + templateUrl: "./fieldset-container.component.html", + styleUrls: [ + "./fieldset-container.component.scss" + ] }) export class FieldsetContainerComponent implements DoCheck, AfterViewInit { diff --git a/src/app/components/generic-calculator/calc-name.component.scss b/src/app/components/generic-calculator/calc-name.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..8e8060ff4581e463b045a4012cef43024867b57c --- /dev/null +++ b/src/app/components/generic-calculator/calc-name.component.scss @@ -0,0 +1,11 @@ +:host { + display: block; +} + +mat-form-field { + width: 100%; + + &.mat-form-field-invalid { + margin-bottom: 1em; + } +} diff --git a/src/app/components/generic-calculator/calc-name.component.ts b/src/app/components/generic-calculator/calc-name.component.ts index 860ed423f6b21c8e557598303328ab4748ff4aef..a682903bd03a6babcb2e6a0c66f14a3169b97607 100644 --- a/src/app/components/generic-calculator/calc-name.component.ts +++ b/src/app/components/generic-calculator/calc-name.component.ts @@ -5,6 +5,9 @@ import { FormulaireDefinition } from "../../formulaire/definition/form-definitio @Component({ selector: "calc-name", templateUrl: "../generic-input/generic-input.component.html", + styleUrls: [ + "./calc-name.component.scss" + ] }) export class CalculatorNameComponent extends GenericInputComponent { @@ -55,13 +58,6 @@ export class CalculatorNameComponent extends GenericInputComponent { return { isValid: valid, message: msg }; } - /** - * convertit le modèle en valeur affichable par l'UI - */ - protected modelToUI(v: any): string { - return v; - } - /** * valide une valeur saisie dans l'UI (forme de la saisie : est ce bien une date, un nombre, ...) * @param ui valide la valeur saisie diff --git a/src/app/components/generic-calculator/calculator.component.html b/src/app/components/generic-calculator/calculator.component.html index 2d00e9c06ad6ee68733e7dcd2808721a5895965e..e2820e129aaa54123c0c44a86e936d815dabd08e 100644 --- a/src/app/components/generic-calculator/calculator.component.html +++ b/src/app/components/generic-calculator/calculator.component.html @@ -1,53 +1,64 @@ -<div class="row"> - <!-- titre --> - <div class="col"> - <!-- on utilise [innerHTML] pour que les codes HTML comme soient interprétés correctement --> - <h1 [innerHTML]="uitextTitre"></h1> - </div> - - <div class="col-sm-3 px-0 mx-0 hyd-window-btns"> - <!-- bouton d'aide --> - <mat-icon *ngIf="enableHelpButton" (click)="openHelp()" color="accent">help</mat-icon> - <!-- bouton de sauvegarde --> - <mat-icon (click)="saveCalculator()">save_alt</mat-icon> - <!-- bouton de fermeture --> - <mat-icon (click)="closeCalculator()">close</mat-icon> - </div> -</div> - -<!-- nom de la calculette --> -<div class="row"> - <div class="col-md-6"> - <calc-name title="Nom de la calculette"></calc-name> - </div> -</div> - -<div class="row"> - <div [ngClass]="(hasResults) ? 'col-12 col-lg-6' : 'col-12'"> - <div class="container-fluid"> - <!-- chapitres --> - <ng-template ngFor let-fe [ngForOf]="formElements"> - <field-set *ngIf="isFieldset(fe)" [style.display]="getFieldsetStyleDisplay(fe.id)" [fieldSet]=fe (radio)=onRadioClick($event) - (validChange)=OnFieldsetValid() (inputChange)=onInputChange()></field-set> - - <fieldset-container *ngIf="isFieldsetContainer(fe)" [_container]=fe (radio)=onRadioClick($event) (validChange)=onFieldsetContainerValid()></fieldset-container> - </ng-template> +<mat-card id="calculator-card"> + + <mat-card-header> + + <div class="hyd-window-btns"> + <!-- bouton d'aide --> + <mat-icon *ngIf="enableHelpButton" (click)="openHelp()" color="accent">help</mat-icon> + <!-- bouton de sauvegarde --> + <mat-icon (click)="saveCalculator()">save_alt</mat-icon> + <!-- bouton de fermeture --> + <mat-icon (click)="closeCalculator()">close</mat-icon> </div> - <!-- bouton calculer --> - <div class="row"> - <div class="col-12 text-center"> - <button type="button" [ngClass]="(isCalculateDisabled) ? 'button_compute_err' : 'button_compute_ok'" name="Calculer" (click)="doCompute()" - [disabled]="isCalculateDisabled">{{ uitextCalculer }}</button> - <p></p> - <p></p> + <!-- titre --> + <!-- on utilise [innerHTML] pour que les codes HTML comme soient interprétés correctement --> + <mat-card-title> + <h1 [innerHTML]="uitextTitre"></h1> + </mat-card-title> + + </mat-card-header> + + <form> + + <mat-card-content> + + <!-- nom de la calculette --> + <calc-name [title]="uitextCalculatorName"></calc-name> + + <div id="calc-cards-container" class="container" fxLayout="row wrap" fxLayoutAlign="space-around start"> + <!-- chapitres --> + <mat-card id="calc-card-field-sets" fxFlex.gt-xs="1 0 400px" fxFlex.lt-sm="1 0 300px"> + <ng-template ngFor let-fe [ngForOf]="formElements"> + <field-set *ngIf="isFieldset(fe)" [style.display]="getFieldsetStyleDisplay(fe.id)" [fieldSet]=fe (radio)=onRadioClick($event) + (validChange)=OnFieldsetValid() (inputChange)=onInputChange()></field-set> + + <fieldset-container *ngIf="isFieldsetContainer(fe)" [_container]=fe (radio)=onRadioClick($event) (validChange)=onFieldsetContainerValid()></fieldset-container> + </ng-template> + + <mat-card-actions> + <!-- bouton calculer --> + <button mat-raised-button color="accent" name="Calculer" (click)="doCompute()"[disabled]="isCalculateDisabled"> + {{ uitextCalculer }} + </button> + </mat-card-actions> + </mat-card> + + <!-- résultats --> + <mat-card id="calc-card-results" fxFlex.gt-xs="1 0 400px" fxFlex.lt-sm="1 0 300px"> + <mat-card-header> + <mat-card-title> + Resultats (à traduire) + </mat-card-title> + </mat-card-header> + <mat-card-content> + <calc-results id="resultsComp" (afterViewChecked)="onCalcResultsViewChecked()"></calc-results> + </mat-card-content> + </mat-card> + </div> - </div> + </mat-card-content> - </div> + </form> - <!-- résultats --> - <div [ngClass]="(hasResults) ? 'col-12 col-lg-6' : 'col-12'"> - <calc-results id="resultsComp" (afterViewChecked)="onCalcResultsViewChecked()"></calc-results> - </div> -</div> +</mat-card> diff --git a/src/app/components/generic-calculator/calculator.component.scss b/src/app/components/generic-calculator/calculator.component.scss index 10f09de7a339c20643881ac17fbb7bfa7e334e5f..699991e24ab4ce9867da4432d7ded52d9c05d46d 100644 --- a/src/app/components/generic-calculator/calculator.component.scss +++ b/src/app/components/generic-calculator/calculator.component.scss @@ -1,20 +1,60 @@ +.hyd-window-btns { + position: absolute; + right: 1em; + text-align: right; - .button_compute_ok { - height: 3em; - width: 30%; + mat-icon { + cursor: pointer; + margin-right: 5px; } +} - .button_compute_err { - height: 3em; - width: 30%; - color: red +#calc-cards-container { + margin-left: -1em; + margin-right: -1em; +} + +mat-card { + margin-bottom: 2em; + + // main card + &#calculator-card { + + > mat-card-header { + margin-bottom: .5em; + padding-right: 6em; // space for right corner buttons + } + } + + // cards inside main card + + &#calc-card-field-sets { + margin-left: 1em; + margin-right: 1em; + + mat-card-actions { + button { + width: 100%; + } + } } - .hyd-window-btns { - text-align: right; + &#calc-card-results { + margin-left: 1em; + margin-right: 1em; + } - mat-icon { - cursor: pointer; - margin-right: 5px; + // @WARNING ::ng-deep est déprécié, mais y a rien d'autre pour + // l'instant (en attente de normalisation par le W3C) + ::ng-deep .mat-card-header-text { + margin: 0; + } + + mat-card-header { + + mat-card-title { + font-size: 16px; + margin-bottom: 8px; } - } \ No newline at end of file + } +} diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts index bdde67531b94434b28f8b54c93b6d4cbeefd1379..e696082cb0e3409287247f1b1810a1b5f17100ef 100644 --- a/src/app/components/generic-calculator/calculator.component.ts +++ b/src/app/components/generic-calculator/calculator.component.ts @@ -138,6 +138,10 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit, return this.intlService.localizeText("INFO_CALCULATOR_CALCULER"); } + public get uitextCalculatorName() { + return this.intlService.localizeText("INFO_CALCULATOR_CALC_NAME"); + } + ngOnInit() { this.intlService.addObserver(this); this.formulaireService.addObserver(this); diff --git a/src/app/components/generic-input/generic-input.component.html b/src/app/components/generic-input/generic-input.component.html index 1f81ebe23535ecddd6fc1f3d1d846c80ec8be0c8..28d062b05ba539d9f0419b166ddae09068d9aaa3 100644 --- a/src/app/components/generic-input/generic-input.component.html +++ b/src/app/components/generic-input/generic-input.component.html @@ -1,6 +1,7 @@ -<div class="md-form form-sm"> - <input mdbInputDirective [mdbValidate]="false" type="text" [id]="inputId" class="form-control" [disabled]="isDisabled" [(ngModel)]="uiValue"> - <!-- on utilise [innerHTML] pour que les codes HTML comme soient interprétés correctement --> - <label [for]="inputId" [innerHTML]="title"></label> - <small *ngIf="showError" class="text-danger" [innerHTML]="errorMessage"></small> -</div> \ No newline at end of file +<mat-form-field> + <input matInput class="form-control" type="text" inputmode="numeric" + [id]="inputId" [disabled]="isDisabled" [(ngModel)]="uiValue" [placeholder]="title" + required> + + <mat-error>{{ errorMessage }}</mat-error> +</mat-form-field> diff --git a/src/app/components/generic-input/generic-input.component.ts b/src/app/components/generic-input/generic-input.component.ts index cd3e9e7dcbfe8478df253eeb4cc1a0b6037caf79..15c56f546c23552b27be30af22e17ab9211b5d8b 100644 --- a/src/app/components/generic-input/generic-input.component.ts +++ b/src/app/components/generic-input/generic-input.component.ts @@ -1,17 +1,7 @@ import { Input, Output, EventEmitter, ChangeDetectorRef, OnChanges } from "@angular/core"; import { BaseComponent } from "../base/base.component"; - -/* -exemple de template : - -<div class="md-form form-sm"> - <input mdbActive type="text" id="form1" class="form-control" [disabled]="isDisabled" - [ngModel]="uiValue" (ngModelChange)="setUIValue($event)"> - <label for="form1">{{title}}</label> - <small class="text-danger">{{_message}}</small> -</div> -*/ +import { isNumeric } from "jalhyd"; /** * classe de gestion générique d'un champ de saisie avec titre, validation et message d'erreur @@ -160,7 +150,7 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC * getter du message d'erreur affiché. * L'erreur de forme (UI) est prioritaire */ - private get errorMessage() { + public get errorMessage() { if (this._errorMessageUI !== undefined) { return this._errorMessageUI; } @@ -268,7 +258,9 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC /** * convertit le modèle en valeur affichable par l'UI */ - protected abstract modelToUI(v: any): string; + protected modelToUI(v: any): string { + return String(v); + } /** * valide une valeur saisie dans l'UI (forme de la saisie : est ce bien une date, un nombre, ...) @@ -276,12 +268,25 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC * @returns isValid : true si la valeur est valide, false sinon * @returns message : message d'erreur */ - protected abstract validateUIValue(ui: string): { isValid: boolean, message: string }; + protected validateUIValue(ui: string): { isValid: boolean, message: string } { + let valid = false; + let msg: string; + + if (! isNumeric(ui)) { + msg = "Veuillez entrer une valeur numérique"; + } else { + valid = true; + } + + return { isValid: valid, message: msg }; + } /** * convertit une valeur saisie dans l'UI en valeur affectable au modèle */ - protected abstract uiToModel(ui: string): any; + protected uiToModel(ui: string): any { + return +ui; + } } /* @@ -298,7 +303,7 @@ import { isNumeric, Message } from "jalhyd"; @Component({ selector: "test-input", template: `<div class="md-form form-sm"> - <input mdbActive type="text" id="form1" class="form-control" [disabled]="isDisabled" [(ngModel)]="uiValue"> + <input type="text" id="form1" class="form-control" [disabled]="isDisabled" [(ngModel)]="uiValue"> <label for="form1">{{title}}</label> <small *ngIf="showError" class="text-danger">{{errorMessage}}</small> </div>` @@ -329,23 +334,6 @@ export class TestInputComponent extends GenericInputComponent { return { isValid: valid, message: msg }; } - protected modelToUI(v: any): string { - if (typeof (v) === "number") - return String(v); - } - - protected validateUIValue(ui: string): { isValid: boolean, message: string } { - let valid: boolean = false; - let msg: string = undefined; - - if (! isNumeric(ui)) - msg = "Veuillez entrer une valeur numérique"; - else - valid = true; - - return { isValid: valid, message: msg }; - } - protected uiToModel(ui: string): any { return +ui; } @@ -359,7 +347,7 @@ import { ParamDefinition } from "jalhyd"; @Component({ selector: "test2-input", template: `<div class="md-form form-sm"> - <input mdbActive type="text" id="form1" class="form-control" [disabled]="isDisabled" [(ngModel)]="uiValue"> + <input type="text" id="form1" class="form-control" [disabled]="isDisabled" [(ngModel)]="uiValue"> <label for="form1">{{title}}</label> <small *ngIf="showError" class="text-danger">{{errorMessage}}</small> </div>` @@ -393,26 +381,5 @@ export class Test2InputComponent extends GenericInputComponent { return { isValid: valid, message: msg }; } - - protected modelToUI(v: any): string { - if (typeof (v) === "number") - return String(v); - } - - protected validateUIValue(ui: string): { isValid: boolean, message: string } { - let valid: boolean = false; - let msg: string = undefined; - - if (! isNumeric(ui)) - msg = "Veuillez entrer une valeur numérique"; - else - valid = true; - - return { isValid: valid, message: msg }; - } - - protected uiToModel(ui: string): any { - return +ui; - } } /**/ diff --git a/src/app/components/generic-select/generic-select.component.html b/src/app/components/generic-select/generic-select.component.html index 8edaab39d123f794448d7abf0090a5cc81ad6419..da7a82515f0406dfc56beb7a36b4e61fc37ac7ac 100644 --- a/src/app/components/generic-select/generic-select.component.html +++ b/src/app/components/generic-select/generic-select.component.html @@ -1,8 +1,7 @@ -<div class="btn-group" dropdown (click)="onSelect($event)"> - <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius> - {{ currentLabel }} - </button> - <div class="dropdown-menu"> - <a class="dropdown-item" *ngFor="let e of entries" [value]=e>{{ entryLabel(e) }}</a> - </div> -</div> \ No newline at end of file +<mat-form-field> + <mat-select [placeholder]="label" [(value)]="selectedValue"> + <mat-option *ngFor="let e of entries" [value]="e"> + {{ entryLabel(e) }} + </mat-option> + </mat-select> +</mat-form-field> diff --git a/src/app/components/generic-select/generic-select.component.ts b/src/app/components/generic-select/generic-select.component.ts deleted file mode 100644 index 6614882ab63ed2f13e6b8fdcfe329dc97f7a6d80..0000000000000000000000000000000000000000 --- a/src/app/components/generic-select/generic-select.component.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Component, Output, EventEmitter } from "@angular/core"; -import { BaseComponent } from "../base/base.component"; - -/* - exemple de template : - -<div class="btn-group" dropdown (click)="onSelect($event)"> - <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius> - {{currentLabel}} - </button> - <div class="dropdown-menu"> - <a class="dropdown-item" *ngFor="let e of entries" [value]=e>{{entryLabel(e)}}</a> - </div> -</div> -*/ - -export abstract class GenericSelectComponent<T> { - - private _currentLabel: string; - - public get currentLabel(): string { - if (! this._currentLabel) { - this._currentLabel = this.selectedLabel; - } - return this._currentLabel; - } - - /** - * appelé quand la valeur courante change dans l'UI - */ - public onSelect(event: any) { - const val = event.target.value; - if (val !== undefined && val !== "") { // might be 0; opening the menu returns "" - this.selectedValue = val; - this._currentLabel = this.selectedLabel; - } - } - - private get selectedLabel(): string { - for (const e of this.entries) { - if (e === this.selectedValue) { - return this.entryLabel(e); - } - } - } - - /** - * liste des objets sélectionnables - */ - public abstract get entries(): any[]; - - /** - * calcule l'étiquette d'une entrée (ce qui est affiché dans la liste déroulante) - */ - protected abstract entryLabel(entry: any): string; - - /** - * valeur actuellement sélectionnée - * la valeur repère une entrée de la liste (cf. [value] dans le template) - */ - public abstract get selectedValue(): T; - - - public abstract set selectedValue(v: T); -} diff --git a/src/app/components/log-entry/log-entry.component.html b/src/app/components/log-entry/log-entry.component.html index 2c2194f95f08e08a3afb090b4cad04b4c5d27e47..4e725d2a88c841262f986d31f5455c7ad93137cf 100644 --- a/src/app/components/log-entry/log-entry.component.html +++ b/src/app/components/log-entry/log-entry.component.html @@ -1,10 +1,10 @@ -<div class="row entry"> - <div class="col-1" style="text-align: center;"> +<div class="entry"> + <div style="text-align: center;"> <mat-icon *ngIf="levelInfo" style="color:green">check_circle</mat-icon> <mat-icon *ngIf="levelWarning" style="color:orange">error_outline</mat-icon> <mat-icon *ngIf="levelError" style="color:red">warning</mat-icon> </div> - <div class="col-11"> + <div> <!-- on utilise [innerHTML] pour que les codes HTML comme soient interprétés correctement --> <span [innerHTML]="text"></span> </div> diff --git a/src/app/components/log/log.component.html b/src/app/components/log/log.component.html index 15d92c0f171cb2dff50912c2fd91978c5af3a986..c2c1364aa186042151630abbe2e7535346d5cfde 100644 --- a/src/app/components/log/log.component.html +++ b/src/app/components/log/log.component.html @@ -1,13 +1,11 @@ -<div class="row" *ngIf="hasEntries"> - <div class="col-12"> - <div class="hyd_log"> - <!-- titre --> - <div class="titre">{{ uitextTitreJournal }}</div> +<div *ngIf="hasEntries"> + <div class="hyd_log"> + <!-- titre --> + <div class="titre">{{ uitextTitreJournal }}</div> - <!-- entrées du journal --> - <ng-template ngFor let-m [ngForOf]="messages"> - <log-entry [_message]=m></log-entry> - </ng-template> - </div> + <!-- entrées du journal --> + <ng-template ngFor let-m [ngForOf]="messages"> + <log-entry [_message]=m></log-entry> + </ng-template> </div> </div> \ No newline at end of file diff --git a/src/app/components/ngparam-input/ngparam-input.component.scss b/src/app/components/ngparam-input/ngparam-input.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..cf6a916663aeed73da3d76250f2cc012463da1e5 --- /dev/null +++ b/src/app/components/ngparam-input/ngparam-input.component.scss @@ -0,0 +1,13 @@ +:host { + display: block; + margin-top: 14px; +} + +mat-form-field { + width: calc(100% - 16px); + margin-right: 16px; + + ::ng-deep input.mat-input-element { + line-height: 1.3em; + } +} diff --git a/src/app/components/ngparam-input/ngparam-input.component.ts b/src/app/components/ngparam-input/ngparam-input.component.ts index 11d004d7514adada257030522afdc89a7f785693..69be65ac350b3d7b23272a41d9d07653c06a1fd0 100644 --- a/src/app/components/ngparam-input/ngparam-input.component.ts +++ b/src/app/components/ngparam-input/ngparam-input.component.ts @@ -10,7 +10,10 @@ import { GenericInputComponent } from "../generic-input/generic-input.component" @Component({ selector: "ngparam-input", - templateUrl: "../generic-input/generic-input.component.html" + templateUrl: "../generic-input/generic-input.component.html", + styleUrls: [ + "./ngparam-input.component.scss" + ] }) export class NgParamInputComponent extends GenericInputComponent implements Observer, OnDestroy { /** @@ -86,27 +89,6 @@ export class NgParamInputComponent extends GenericInputComponent implements Obse return { isValid: valid, message: msg }; } - protected modelToUI(v: any): string { - return String(v); - } - - protected validateUIValue(ui: string): { isValid: boolean, message: string } { - let valid = false; - let msg: string; - - if (! isNumeric(ui)) { - msg = "Veuillez entrer une valeur numérique"; - } else { - valid = true; - } - - return { isValid: valid, message: msg }; - } - - protected uiToModel(ui: string) { - return +ui; - } - public update(sender: any, data: any): void { switch (data["action"]) { case "ngparamAfterValue": diff --git a/src/app/components/param-computed/param-computed.component.html b/src/app/components/param-computed/param-computed.component.html new file mode 100644 index 0000000000000000000000000000000000000000..0751e36bf1dc93de19a451f8aefd886085dc6130 --- /dev/null +++ b/src/app/components/param-computed/param-computed.component.html @@ -0,0 +1,7 @@ +<!-- a fake input bound to nothing, for the sake of UI consistency --> +<mat-form-field> + <input matInput disabled class="form-control" type="text" [ngModel]="infoText" [placeholder]="param.title"> + <button *ngIf="isDicho" mat-icon-button class="param-computed-more" (click)="openDialog()"> + <mat-icon>more_horiz</mat-icon> + </button> +</mat-form-field> diff --git a/src/app/components/param-computed/param-computed.component.scss b/src/app/components/param-computed/param-computed.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..100e77a8198ef0f0c71528da0bfa1d3cac9a97c9 --- /dev/null +++ b/src/app/components/param-computed/param-computed.component.scss @@ -0,0 +1,21 @@ +:host { + display: block; + margin-top: 14px; + + mat-form-field { + width: calc(100% - 16px); + margin-right: 16px; + + ::ng-deep input.mat-input-element { + line-height: 1.3em; + width: calc(100% - 40px); + text-overflow: ellipsis; + } + + .param-computed-more { + position: absolute; + bottom: 0; + right: 0; + } + } +} diff --git a/src/app/components/param-computed/param-computed.component.ts b/src/app/components/param-computed/param-computed.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..a1148d90b5b25d7256215e82e6509cde692791d2 --- /dev/null +++ b/src/app/components/param-computed/param-computed.component.ts @@ -0,0 +1,50 @@ +import { Component, Input } from "@angular/core"; +import { MatDialog } from "@angular/material"; +import { NgParameter } from "../../formulaire/ngparam"; +import { ParamCalculability } from "jalhyd"; +import { DialogEditParamComputedComponent } from "../dialog-edit-param-computed/dialog-edit-param-computed.component"; + +@Component({ + selector: "param-computed", + templateUrl: "./param-computed.component.html", + styleUrls: [ + "./param-computed.component.scss" + ] +}) +export class ParamComputedComponent { + + @Input() + public param: NgParameter; + + @Input() + public title: string; + + constructor( + private editInitialValueDialog: MatDialog + ) { } + + public get isDicho() { + return this.param.paramDefinition.calculability === ParamCalculability.DICHO; + } + + public get infoText() { + let text = "En calcul (à traduire)"; // @TODO traduire "in calculation" + if (this.isDicho) { + text += " (valeur initiale: " + this.param.getValue() + ")"; + } + return text; + } + + public openDialog() { + // modification de la valeur initiale, sans avoir à remettre le mode de + // paramètre sur "fixé" + this.editInitialValueDialog.open( + DialogEditParamComputedComponent, + { + data: { + param: this.param + } + } + ); + } +} diff --git a/src/app/components/param-field-line/param-field-line.component.html b/src/app/components/param-field-line/param-field-line.component.html index 41c5d1fead0e3e3405def7577ef33328870a42f3..90023511ea7da6245a770dee2e14cfe929fe8689 100644 --- a/src/app/components/param-field-line/param-field-line.component.html +++ b/src/app/components/param-field-line/param-field-line.component.html @@ -1,43 +1,49 @@ -<div class="row"> - <!-- input de saisie de la valeur --> - <div [ngClass]="(formHasResults) ? 'col-xl-6 pt-3':'col-md-6 col-xl-8 pt-3'"> - <ngparam-input [_inputDisabled]="isInputDisabled" [title]="title" (change)="onInputChange($event)"></ngparam-input> - </div> - <div class="btn-group col" role="group"> - <!-- px-3 : padding left/right 3 --> - <!-- py-3 : padding top/bottom 3 --> - <!-- h-50 : hauteur relative de l'élément par rapport au parent à 50%--> - <!-- cf. https://getbootstrap.com/docs/4.0/utilities/spacing --> +<div class="container" fxLayout="row wrap" fxLayoutAlign="space-between start"> + + <!-- input de saisie de la valeur --> + <div fxFlex="1 0 120px"> + <!-- composant pour gérer le cas général (valeur numérique à saisir) --> + <ngparam-input [title]="param.title" [hidden]="! isRadioFixChecked" (change)="onInputChange($event)"></ngparam-input> + + <!-- composant pour gérer le cas "paramètre calculé" --> + <param-computed *ngIf="isRadioCalChecked" [title]="title" [param]="param"></param-computed> - <!-- radio "fixé" --> - <label *ngIf="hasRadioFix()" class="{{radioFixClass}} h-75 px-3 py-3" [(ngModel)]="radioModel" mdbRadio="Left" name="radio_param_{{symbol}}" - value="fix" (click)="onRadioClick('fix')" [checked]=radioFixCheck [disabled]=isDisabled id="radio_fix"> - {{ uitextParamFixe }} - </label> + <!-- composant pour gérer le cas "paramètre à varier" (min-max/liste de valeurs) --> + <param-values *ngIf="isRadioVarChecked" [title]="title" [param]="param" (valid)=onParamValuesValid($event)></param-values> + + <!-- composant pour gérer le cas "paramètre lié" --> + <param-link *ngIf="isRadioLinkChecked" [title]="title" [param]="param" (valid)=onParamValuesValid($event)></param-link> + </div> - <!-- radio "varier" --> - <label *ngIf="hasRadioVar()" class="{{radioVarClass}} h-75 px-3 py-3" [(ngModel)]="radioModel" mdbRadio="Middle" name="radio_param_{{symbol}}" - value="var" (click)="onRadioClick('var')" [checked]=radioVarCheck [disabled]=isDisabled id="radio_var"> - {{ uitextParamVarier }} - </label> + <div class="toggle-group-container" fxFlex="0 0 auto"> + <mat-button-toggle-group> - <!-- radio "calculer" --> - <label *ngIf="hasRadioCal()" class="{{radioCalClass}} h-75 px-3 py-3" [(ngModel)]="radioModel" mdbRadio="Right" name="radio_param_{{symbol}}" - value="cal" (click)="onRadioClick('cal')" [checked]=radioCalCheck [disabled]=isDisabled id="radio_cal"> - {{ uitextParamCalculer }} - </label> + <mat-button-toggle id="radio_fix" value="radio_fix" *ngIf="hasRadioFix()" + (click)="onRadioClick('fix')" [checked]="isRadioFixChecked"> + <span fxHide.xxs>{{ uitextParamFixe }}</span> + <span fxHide.gt-xxs>F</span> + </mat-button-toggle> + + <mat-button-toggle id="radio_var" value="radio_var" *ngIf="hasRadioVar()" + (click)="onRadioClick('var')" [checked]="isRadioVarChecked"> + <span fxHide.xxs>{{ uitextParamVarier }}</span> + <span fxHide.gt-xxs>V</span> + </mat-button-toggle> + + <mat-button-toggle id="radio_cal" value="radio_cal" *ngIf="hasRadioCal()" + (click)="onRadioClick('cal')" [checked]="isRadioCalChecked"> + <span fxHide.xxs>{{ uitextParamCalculer }}</span> + <span fxHide.gt-xxs>C</span> + </mat-button-toggle> + + <mat-button-toggle id="radio_link" value="radio_link" *ngIf="hasRadioLink()" + (click)="onRadioClick('link')" [checked]="isRadioLinkChecked"> + <span fxHide.xxs>{{ uitextParamLie }}</span> + <span fxHide.gt-xxs>L</span> + </mat-button-toggle> - <!-- radio "lié" --> - <label *ngIf="hasRadioLink()" class="{{radioLinkClass}} h-75 px-3 py-3" [(ngModel)]="radioModel" mdbRadio="Right" name="radio_param_{{symbol}}" - value="link" (click)="onRadioClick('link')" [checked]=radioLinkCheck [disabled]=isDisabled id="radio_link"> - {{ uitextParamLie }} - </label> + </mat-button-toggle-group> </div> -</div> -<!-- composant pour gérer le cas "paramètre à varier" (min-max/liste de valeurs) --> -<param-values *ngIf="isRadioVarChecked" [param]="param" (valid)=onParamValuesValid($event)></param-values> - -<!-- composant pour gérer le cas "paramètre lié" --> -<param-link *ngIf="isRadioLinkChecked" [param]="param" (valid)=onParamValuesValid($event)></param-link> \ No newline at end of file +</div> diff --git a/src/app/components/param-field-line/param-field-line.component.scss b/src/app/components/param-field-line/param-field-line.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..4cfe3dad92ebfe608a3bef8eda844f0af394d3f4 --- /dev/null +++ b/src/app/components/param-field-line/param-field-line.component.scss @@ -0,0 +1,13 @@ +.toggle-group-container { + text-align: right; +} + +mat-button-toggle-group { + margin-top: 4px; + box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12); + + /*::ng-deep .mat-button-toggle-label-content { + line-height: 32px; + padding: 0 10px; + }*/ +} diff --git a/src/app/components/param-field-line/param-field-line.component.ts b/src/app/components/param-field-line/param-field-line.component.ts index 82608891a6bc58b9f5fe9135a6f89b7930ac7e66..50f368d0776457e65cc540d10e68bb22ea18d0ba 100644 --- a/src/app/components/param-field-line/param-field-line.component.ts +++ b/src/app/components/param-field-line/param-field-line.component.ts @@ -7,25 +7,16 @@ import { ServiceFactory } from "../../services/service-factory"; import { ParamValueMode, CalculatorType, ParallelStructure } from "jalhyd"; import { FormulaireService } from "../../services/formulaire/formulaire.service"; import { ParamLinkComponent } from "../param-link/param-link.component"; +import { ParamComputedComponent } from "../param-computed/param-computed.component"; +/** + * Sélecteur de mode pour chaque paramètre: fixé, varier, calculer, lié + */ @Component({ selector: "param-field-line", templateUrl: "./param-field-line.component.html", - styles: [ - `.btn-on { - color:#505050; - border: 3px solid #505050; - background-color:white; - text-transform: uppercase; - font-size: 0.8em; - }`, - `.btn-off { - color:white; - border: 3px solid #505050; - background-color:#505050; - text-transform: uppercase; - font-size: 0.8em; - }` + styleUrls: [ + "./param-field-line.component.scss" ] }) export class ParamFieldLineComponent implements OnChanges { @@ -37,17 +28,6 @@ export class ParamFieldLineComponent implements OnChanges { this.inputChange = new EventEmitter(); } - public get title(): string { - let t = ""; - if (this.param.label !== undefined) { - t = this.param.label; - } - if (this.param.unit !== undefined && this.param.unit !== "") { - t = t + " (" + this.param.unit + ")"; - } - return t; - } - private get uitextParamFixe() { return this.intlService.localizeText("INFO_PARAMFIELD_PARAMFIXE"); } @@ -71,110 +51,36 @@ export class ParamFieldLineComponent implements OnChanges { return this.param.symbol; } - /** - * calcule l'état du radio "paramètre fixé" - */ + // états des boutons pour l'interface + private get radioFixCheck(): string { return this.isRadioFixChecked ? "checked" : undefined; } - - /** - * calcule l'état du radio "paramètre à varier" - */ private get radioVarCheck(): string { return this.isRadioVarChecked ? "checked" : undefined; } - - /** - * calcule l'état du radio "paramètre à calculer" - */ private get radioCalCheck(): string { - if (this.param.radioState === ParamRadioConfig.CAL) { - return "checked"; - } + return this.isRadioCalChecked ? "checked" : undefined; } - - /** - * calcule l'état du radio "paramètre lié" - */ private get radioLinkCheck(): string { - if (this.param.radioState === ParamRadioConfig.LINK) { - return "checked"; - } + return this.isRadioLinkChecked ? "checked" : undefined; } - /** - * retourne l'état du radio "paramètre fixé" sous forme booléenne - */ + // états booléens des boutons + public get isRadioFixChecked(): boolean { return this.param.radioState === ParamRadioConfig.FIX; } - - /** - * retourne l'état du radio "paramètre à varier" sous forme booléenne - */ public get isRadioVarChecked(): boolean { return this.param.radioState === ParamRadioConfig.VAR; } - - /** - * retourne l'état du radio "paramètre lié" sous forme booléenne - */ + public get isRadioCalChecked(): boolean { + return this.param.radioState === ParamRadioConfig.CAL; + } public get isRadioLinkChecked(): boolean { return this.param.radioState === ParamRadioConfig.LINK; } - /** - * désactivation de tous les boutons radio si paramètre par défaut à "CAL" - */ - private get isDisabled(): boolean { - return this.param.isDefault && this.param.radioState === ParamRadioConfig.CAL; - } - - /** - * désactivation du champ de saisie - */ - public get isInputDisabled(): boolean { - return this.param.radioState !== ParamRadioConfig.FIX; - } - - private get radioFixClass(): string { - if (this.on) { - return this.radioFixCheck ? this.onClass : this.offClass; - } - return ""; - } - - /** - * classe du radio "varier" - */ - private get radioVarClass(): string { - if (this.on) { - return this.radioVarCheck ? this.onClass : this.offClass; - } - return ""; - } - - /** - * classe du radio "calculer" - */ - private get radioCalClass(): string { - if (this.on) { - return this.radioCalCheck ? this.onClass : this.offClass; - } - return ""; - } - - /** - * classe du radio "lié" - */ - private get radioLinkClass(): string { - if (this.on) { - return this.radioLinkCheck ? this.onClass : this.offClass; - } - return ""; - } - /** * validité des saisies du composant */ @@ -186,6 +92,10 @@ export class ParamFieldLineComponent implements OnChanges { case ParamRadioConfig.VAR: return this._isRangeValid; + case ParamRadioConfig.LINK: + // at first this._paramLinkComponent is undefined until view is refreshed + return this._paramLinkComponent ? this._paramLinkComponent.isValid : true; + default: return true; } @@ -196,11 +106,14 @@ export class ParamFieldLineComponent implements OnChanges { } @Input() - private param: NgParameter; + public param: NgParameter; @ViewChild(NgParamInputComponent) private _ngParamInputComponent: NgParamInputComponent; + @ViewChild(ParamComputedComponent) + private _computedParamComponent: ParamComputedComponent; + @ViewChild(ParamLinkComponent) private _paramLinkComponent: ParamLinkComponent; @@ -210,14 +123,10 @@ export class ParamFieldLineComponent implements OnChanges { @Output() private inputChange: EventEmitter<void>; - /** - * true si la valeur saisie est valide - */ + /** true si la valeur saisie est valide */ private _isInputValid = false; - /** - * true si le min-max/liste est valide - */ + /** true si le min-max/liste est valide */ private _isRangeValid = true; private intlService: I18nService; @@ -229,7 +138,6 @@ export class ParamFieldLineComponent implements OnChanges { * envoi d'un message au composant parent * cf. https://angular.io/guide/component-interaction#parent-listens-for-child-event */ - @Output() private radio = new EventEmitter<any>(); @@ -246,7 +154,7 @@ export class ParamFieldLineComponent implements OnChanges { public hasRadioFix(): boolean { switch (this.param.radioConfig) { case ParamRadioConfig.FIX: - return this.hasRadioLink(); + return this.hasRadioLink(); // gné ? default: return true; @@ -304,16 +212,19 @@ export class ParamFieldLineComponent implements OnChanges { private onRadioClick(option: string) { const oldValue = this.param.valueMode; - switch (option) { case "fix": - const oldValueMode = this.param.valueMode; this.param.valueMode = ParamValueMode.SINGLE; + // @WTF why do we reset the value here ? this.param.setValue(this, this.param.paramDefinition.paramValues.singleValue); break; case "var": - this.param.valueMode = ParamValueMode.MINMAX; // min/max par défaut + // prevent setting LISTE mode back to MINMAX if someone clicks "variable" again + // after setting variable mode to LISTE + if (oldValue !== ParamValueMode.MINMAX && oldValue !== ParamValueMode.LISTE) { + this.param.valueMode = ParamValueMode.MINMAX; // min/max par défaut + } break; case "cal": @@ -324,12 +235,10 @@ export class ParamFieldLineComponent implements OnChanges { this.param.valueMode = ParamValueMode.LINK; break; } - this.radio.emit({ "param": this.param, "oldValueMode": oldValue }); - // MAJ validité this.emitValidity(); } diff --git a/src/app/components/param-link/param-link.component.html b/src/app/components/param-link/param-link.component.html index f7289b7393df1263526546072753c8c72459fd87..5b198816bf96e4b7b3df1aec2f428f7e681d1ec2 100644 --- a/src/app/components/param-link/param-link.component.html +++ b/src/app/components/param-link/param-link.component.html @@ -1,13 +1,10 @@ -<div class="row"> - <div class="btn-group col-6 col-sm-3" dropdown (click)="onSelectLinkableParam($event)"> - <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius> - {{ currentLinkedParamLabel }} - </button> - <div class="dropdown-menu"> - <a class="dropdown-item" *ngFor="let e of linkableParams" [value]=e>{{ selectItemLabel(e) }}</a> - </div> - </div> - <div class="col-6 text-danger"> +<mat-form-field> + <mat-select [name]='"linked-param_" + param.uid' required [placeholder]="param.title" [(ngModel)]="currentLinkedParam"> + <mat-option *ngFor="let e of linkableParams" [value]="e"> + {{ selectItemLabel(e) }} + </mat-option> + </mat-select> + <mat-error> {{ message }} - </div> -</div> \ No newline at end of file + </mat-error> +</mat-form-field> diff --git a/src/app/components/param-link/param-link.component.scss b/src/app/components/param-link/param-link.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..d4cfda5d9b29ae602ee12716a9cd6f4a7af1c574 --- /dev/null +++ b/src/app/components/param-link/param-link.component.scss @@ -0,0 +1,23 @@ +:host { + display: block; + // width: 100%; + // width: 70%; min-width: 264px; // for smallest screens (360) + // get the select closer to the related param line above + margin-top: 14px; + + mat-form-field { + width: calc(100% - 16px); + margin-right: 16px; + + mat-select { + + ::ng-deep .mat-select-value { + > span { + > span { + line-height: 1.3em; + } + } + } + } + } +} diff --git a/src/app/components/param-link/param-link.component.ts b/src/app/components/param-link/param-link.component.ts index b0246dee55b77439a1e2c7b1e2f293a08e7a3d5d..4add3db0526ea2e31dee6f436bab280a8b119f2b 100644 --- a/src/app/components/param-link/param-link.component.ts +++ b/src/app/components/param-link/param-link.component.ts @@ -7,12 +7,18 @@ import { FormulaireService } from "../../services/formulaire/formulaire.service" @Component({ selector: "param-link", - templateUrl: "./param-link.component.html" + templateUrl: "./param-link.component.html", + styleUrls: [ + "./param-link.component.scss" + ] }) export class ParamLinkComponent implements OnChanges, Observer, OnDestroy { // paramètre géré (qui sera lié à une valeur, cad qui importe cette valeur) @Input() - private param: NgParameter; + public param: NgParameter; + + @Input() + public title: string; @Output() private valid: EventEmitter<boolean>; @@ -47,6 +53,8 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy { private _formService: FormulaireService; + public label = "Choix du paramètre lié (à traduire)"; + constructor() { this.valid = new EventEmitter(); this._formService = ServiceFactory.instance.formulaireService; @@ -61,27 +69,21 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy { return this._message; } - /** - * envoi d'un événement de validité - */ - private emitValidity() { - // this.valid.emit(this._validList); + public set currentLinkedParam(p: any) { + for (let i = 0; i < this._linkableParams.length; i++) { + if (this._linkableParams[i].value.uid === p.uid) { + this.linkTo(i); + break; + } else { + i++; + } + } } - /** - * réception d'un événement du menu des paramètres liables - */ - public onSelectLinkableParam(event: any) { - const next = event.target.value; - if (next !== undefined && next !== "") { // opening the dropdown returns "" - let i = 0; - for (const e of this._linkableParams) { - if (this._linkableParams[i].value.uid === next.value.uid) { - this.linkTo(i); - break; - } else { - i++; - } + public get currentLinkedParam() { + if (this._linkableParams !== undefined) { + if (this._currentIndex !== -1 && this._currentIndex < this._linkableParams.length) { + return this._linkableParams[this._currentIndex]; } } } @@ -90,13 +92,16 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy { * valeur courante affichée dans le select des paramètres liables */ public get currentLinkedParamLabel(): string { - if (this._linkableParams !== undefined) { - if (this._currentIndex !== -1 && this._currentIndex < this._linkableParams.length) { - return this.selectItemLabel(this._linkableParams[this._currentIndex]); - } + const clp = this.currentLinkedParam(); + if (clp) { + return this.selectItemLabel(clp); } } + public get isValid(): boolean { + return this._currentIndex !== -1 && this.param.isValid; + } + /** * attribut "label" d'une entrée du select des paramètres */ diff --git a/src/app/components/param-values/ngparam-max.component.ts b/src/app/components/param-values/ngparam-max.component.ts deleted file mode 100644 index d878dd68b385f2e02267ce9eff4d3d67dc34cb51..0000000000000000000000000000000000000000 --- a/src/app/components/param-values/ngparam-max.component.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Component, Input, ChangeDetectorRef } from "@angular/core"; - -import { isNumeric } from "jalhyd"; - -import { GenericInputComponent } from "../generic-input/generic-input.component"; -import { I18nService } from "../../services/internationalisation/internationalisation.service"; -import { NgParameter } from "../../formulaire/ngparam"; - -@Component({ - selector: "ngparam-max", - templateUrl: "../generic-input/generic-input.component.html" -}) -export class NgParamMaxComponent extends GenericInputComponent { - constructor(private intlService: I18nService, cdRef: ChangeDetectorRef) { - super(cdRef); - } - - /** - * paramètre géré - */ - private get _param(): NgParameter { - return this._model; - } - - protected getModelValue(): any { - if (this._param) { - return this._param.maxValue; - } - } - - protected setModelValue(sender: any, v: any) { - this._param.maxValue = v; - } - - protected validateModelValue(v: any): { isValid: boolean, message: string } { - let msg; - let valid = false; - - if (this._param === undefined) { - msg = "internal error, model undefined"; - } else { - if (!this._param.checkMax(v)) { - msg = "La valeur n'est pas dans ]" + this._param.minValue + " , " + this._param.domain.maxValue + "]"; - } else { - valid = true; - } - } - - return { isValid: valid, message: msg }; - } - - protected modelToUI(v: any): string { - if (typeof (v) === "number") { - return String(v); - } - } - - protected validateUIValue(ui: string): { isValid: boolean, message: string } { - let valid = false; - let msg: string; - - if (! isNumeric(ui)) { - msg = "Veuillez entrer une valeur numérique"; - } else { - valid = true; - } - - return { isValid: valid, message: msg }; - } - - protected uiToModel(ui: string) { - return +ui; - } -} diff --git a/src/app/components/param-values/ngparam-min.component.ts b/src/app/components/param-values/ngparam-min.component.ts deleted file mode 100644 index 687df8d0ecefb457a1456af08cc45b9d1c1ccc84..0000000000000000000000000000000000000000 --- a/src/app/components/param-values/ngparam-min.component.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Component, Input, ChangeDetectorRef } from "@angular/core"; - -import { isNumeric } from "jalhyd"; - -import { GenericInputComponent } from "../generic-input/generic-input.component"; -import { I18nService } from "../../services/internationalisation/internationalisation.service"; -import { NgParameter } from "../../formulaire/ngparam"; - -@Component({ - selector: "ngparam-min", - templateUrl: "../generic-input/generic-input.component.html" -}) -export class NgParamMinComponent extends GenericInputComponent { - constructor(private intlService: I18nService, cdRef: ChangeDetectorRef) { - super(cdRef); - } - - /** - * paramètre géré - */ - private get _param(): NgParameter { - return this._model; - } - - protected getModelValue(): any { - if (this._param) { - return this._param.minValue; - } - } - - protected setModelValue(sender: any, v: any) { - this._param.minValue = v; - } - - protected validateModelValue(v: any): { isValid: boolean, message: string } { - let msg: string; - let valid = false; - - if (this._param === undefined) { - msg = "internal error, model undefined"; - } else { - if (!this._param.checkMin(v)) { - msg = "La valeur n'est pas dans [" + this._param.domain.minValue + " , " + this._param.maxValue + "["; - } else { - valid = true; - } - } - - return { isValid: valid, message: msg }; - } - - protected modelToUI(v: any): string { - if (typeof (v) === "number") { - return String(v); - } - } - - protected validateUIValue(ui: string): { isValid: boolean, message: string } { - let valid = false; - let msg: string; - - if (! isNumeric(ui)) { - msg = "Veuillez entrer une valeur numérique"; - } else { - valid = true; - } - - return { isValid: valid, message: msg }; - } - - protected uiToModel(ui: string): any { - return +ui; - } -} diff --git a/src/app/components/param-values/ngparam-step.component.ts b/src/app/components/param-values/ngparam-step.component.ts deleted file mode 100644 index ac0fd27fd70cacf6071ed5ff0dedac9a1c3d5db0..0000000000000000000000000000000000000000 --- a/src/app/components/param-values/ngparam-step.component.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Component, Input, ChangeDetectorRef } from "@angular/core"; - -import { isNumeric } from "jalhyd"; - -import { GenericInputComponent } from "../generic-input/generic-input.component"; -import { I18nService } from "../../services/internationalisation/internationalisation.service"; -import { NgParameter } from "../../formulaire/ngparam"; - -@Component({ - selector: "ngparam-step", - templateUrl: "../generic-input/generic-input.component.html" -}) -export class NgParamStepComponent extends GenericInputComponent { - constructor(private intlService: I18nService, cdRef: ChangeDetectorRef) { - super(cdRef); - } - - /** - * paramètre géré - */ - private get _param(): NgParameter { - return this._model; - } - - protected getModelValue(): any { - if (this._param) { - return this._param.stepValue; - } - } - - protected setModelValue(sender: any, v: any) { - this._param.stepValue = v; - } - - protected validateModelValue(v: any): { isValid: boolean, message: string } { - let msg: string; - let valid = false; - - if (! this._param) { - msg = "internal error, model undefined"; - } else { - if (this._param.isMinMaxValid) { - if (!this._param.checkStep(v)) { - msg = "La valeur n'est pas dans " + this._param.stepRefValue.toString(); - } else { - valid = v > 0; - if (!valid) { - msg = "La valeur ne peut pas être <= 0"; - } - } - } else { - msg = "Veuillez corriger le min/max"; - } - } - - return { isValid: valid, message: msg }; - } - - protected modelToUI(v: any): string { - if (typeof (v) === "number") { - return String(v); - } - return "<invalid>"; - } - - protected validateUIValue(ui: string): { isValid: boolean, message: string } { - let valid = false; - let msg: string; - - if (! isNumeric(ui)) { - msg = "Veuillez entrer une valeur numérique"; - } else { - valid = true; - } - - return { isValid: valid, message: msg }; - } - - protected uiToModel(ui: string) { - return +ui; - } -} diff --git a/src/app/components/param-values/param-values.component.html b/src/app/components/param-values/param-values.component.html index f4dc496878e94c63d55d6e5e1b59bc277430f16d..37cda318bfbb8ac4581d6f45ab2188656e7f89fd 100644 --- a/src/app/components/param-values/param-values.component.html +++ b/src/app/components/param-values/param-values.component.html @@ -1,24 +1,7 @@ -<div class="row"> - <div class="btn-group col-12 col-sm-3" dropdown (click)="onSelectValueMode($event)"> - <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius> - {{ currentModeSelectLabel }} - </button> - <div class="dropdown-menu"> - <a class="dropdown-item" *ngFor="let e of valueModes" [value]=e.value>{{ e.label }}</a> - </div> - </div> - - <div *ngIf="isMinMax" class="col-12 col-sm-3"> - <ngparam-min [title]="uitextValeurMini" (onChange)="onMinChanged($event)"></ngparam-min> - </div> - <div *ngIf="isMinMax" class="col-12 col-sm-3"> - <ngparam-max [title]="uitextValeurMaxi" (onChange)="onMaxChanged($event)"></ngparam-max> - </div> - <div *ngIf="isMinMax" class="col-12 col-sm-3"> - <ngparam-step [title]="uitextPasVariation" [param]="param" (onChange)="onStepChanged($event)"></ngparam-step> - </div> - - <div *ngIf="isList" class="col-12 col-sm-6"> - <value-list title="valeurs séparées par ';'" (onChange)="onListChanged($event)"></value-list> - </div> -</div> \ No newline at end of file +<!-- a fake input bound to nothing, for the sake of UI consistency --> +<mat-form-field> + <input matInput disabled class="form-control" type="text" [ngModel]="infoText" [placeholder]="param.title"> + <button mat-icon-button class="param-values-more" (click)="openDialog()"> + <mat-icon>more_horiz</mat-icon> + </button> +</mat-form-field> diff --git a/src/app/components/param-values/param-values.component.scss b/src/app/components/param-values/param-values.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..720b9b04ac2709bcc2d7f0e948b5f725b7596afb --- /dev/null +++ b/src/app/components/param-values/param-values.component.scss @@ -0,0 +1,21 @@ +:host { + display: block; + margin-top: 14px; + + mat-form-field { + width: calc(100% - 16px); + margin-right: 16px; + + ::ng-deep input.mat-input-element { + line-height: 1.3em; + width: calc(100% - 40px); + text-overflow: ellipsis; + } + + .param-values-more { + position: absolute; + bottom: 0; + right: 0; + } + } +} diff --git a/src/app/components/param-values/param-values.component.ts b/src/app/components/param-values/param-values.component.ts index d7f2b4b57a7ff4176a9398a5ef785e675cfdd902..221442da9c54f1aff8b4c1d63e69cca553ffcee9 100644 --- a/src/app/components/param-values/param-values.component.ts +++ b/src/app/components/param-values/param-values.component.ts @@ -1,361 +1,67 @@ -import { Component, Input, Output, EventEmitter, ViewChild, AfterViewChecked, OnChanges } from "@angular/core"; - -import { ParamValueMode } from "jalhyd"; - -import { I18nService } from "../../services/internationalisation/internationalisation.service"; +import { Component, Input, AfterViewInit } from "@angular/core"; import { NgParameter } from "../../formulaire/ngparam"; -import { NgParamMinComponent } from "./ngparam-min.component"; -import { NgParamMaxComponent } from "./ngparam-max.component"; -import { NgParamStepComponent } from "./ngparam-step.component"; -import { BaseComponent } from "../base/base.component"; -import { ValueListComponent } from "./value-list.component"; +import { DialogEditParamValuesComponent } from "../dialog-edit-param-values/dialog-edit-param-values.component"; +import { MatDialog } from "@angular/material"; +import { ParamValueMode } from "jalhyd"; @Component({ selector: "param-values", templateUrl: "./param-values.component.html", - styles: [ - `.btn-on { - color:#505050; - border: 3px solid #505050; - background-color:white; - text-transform: uppercase; - font-size: 0.8em; - }`, - `.btn-off { - color:white; - border: 3px solid #505050; - background-color:#505050; - text-transform: uppercase; - font-size: 0.8em; - }` + styleUrls: [ + "./param-values.component.scss" ] }) -export class ParamValuesComponent extends BaseComponent implements AfterViewChecked, OnChanges { - @Input() - private param: NgParameter; - - private _valueModes = []; - - /** - * true quand les champs du composant et de ses enfants sont initialisés - */ - private _initCompleted = false; - - /** - * true si la valeur min est valide - */ - private _validMin = false; - - /** - * true si la valeur max est valide - */ - private _validMax = false; - - /** - * true si la valeur du pas est valide - */ - private _validStep = false; - - /** - * true si la liste de valeurs est valide - */ - private _validList = false; - - /** - * flag signalant qu'il faut initialiser le composant ValueListComponent (par ex quand on a sélectionné le mode "liste") - */ - private _doInitList = false; - - /** - * flag signalant qu'il faut initialiser les composants min/max/pas - */ - private _doInitMinmax = false; - - /** - * composant de saisie du minimum - */ - @ViewChild(NgParamMinComponent) - private _minComponent: NgParamMinComponent; - - /** - * composant de saisie du maximum - */ - @ViewChild(NgParamMaxComponent) - private _maxComponent: NgParamMaxComponent; - - /** - * composant de saisie du pas de variation - */ - @ViewChild(NgParamStepComponent) - private _stepComponent: NgParamStepComponent; - - /** - * composant de saisie d'une liste de valeurs - */ - @ViewChild(ValueListComponent) - private _listComponent: ValueListComponent; - - @Output() - private valid: EventEmitter<boolean>; - - constructor(private intlService: I18nService) { - super(); - this._valueModes.push({ "value": ParamValueMode.MINMAX, "label": "Min/max" }); - this._valueModes.push({ "value": ParamValueMode.LISTE, "label": "Liste" }); - this.valid = new EventEmitter(); - } - - /** - * init des champs min/max/pas - */ - private initMinMaxStep() { - if (this.isMinMax && this._doInitMinmax) { - this._doInitMinmax = false; - - // valeur pour min : celle déjà définie ou celle déduite de la valeur saisie - let min: number = this.param.minValue; - if (min === undefined) { - min = this.param.getValue() / 2; - } - - // valeur pour max : celle déjà définie ou celle déduite de la valeur saisie - let max: number = this.param.maxValue; - if (max === undefined) { - max = this.param.getValue() * 2; - } - - this.param.minValue = min; - this._minComponent.model = this.param; - - this.param.maxValue = max; - this._maxComponent.model = this.param; - - // valeur du pas - let step = this.param.stepValue; - if (step === undefined) { - step = (max - min) / 20; - } - this.param.stepValue = step; - this._stepComponent.model = this.param; - - this.validateAll(); - - this._validMin = this._minComponent.isValid; - this._validMax = this._maxComponent.isValid; - this._validStep = this._stepComponent.isValid; - this.emitValidity(); - - this._initCompleted = true; - } - } - - /** - * initialisation de la liste de valeurs avec celle du paramètre géré - */ - private initList() { - if (this._doInitList && this._listComponent !== undefined) { - this._doInitList = false; - let l = this.param.valueList; - if (l === undefined) { - if (this.param.isDefined) { - l = [this.param.getValue()]; - } else { - l = []; - } - } - - this.param.valueList = l; - this._listComponent.model = this.param; - } - } - - /** - * revalidation de tous les composants enfants - */ - private validateAll() { - if (this._minComponent !== undefined) { - this._minComponent.validate(); - } - if (this._maxComponent !== undefined) { - this._maxComponent.validate(); - } - if (this._stepComponent !== undefined) { - this._stepComponent.validate(); - } - if (this._listComponent !== undefined) { - this._listComponent.validate(); - } - } +export class ParamValuesComponent implements AfterViewInit { - /** - * envoi d'un événement de validité - */ - private emitValidity() { - switch (this.param.valueMode) { - case ParamValueMode.LISTE: - this.valid.emit(this._validList); - break; - - case ParamValueMode.MINMAX: - this.valid.emit(this._validMin && this._validMax && this._validStep); - break; - } - } - - /** - * réception d'un événement de NgParamMinComponent - */ - private onMinChanged(event: any) { - if (this._initCompleted) { - switch (event.action) { - case "model": - this.validateAll(); - break; - - case "valid": - this._validMin = event.value; - this.emitValidity(); - break; - } - } - } - - /** - * réception d'un événement de NgParamMaxComponent - */ - private onMaxChanged(event: any) { - if (this._initCompleted) { - switch (event.action) { - case "model": - this.validateAll(); - break; - - case "valid": - this._validMax = event.value; - this.emitValidity(); - break; - } - } - } - - /** - * réception d'un événement de NgParamStepComponent - */ - private onStepChanged(event: any) { - if (this._initCompleted) { - switch (event.action) { - case "model": - this.validateAll(); - break; - - case "valid": - this._validStep = event.value; - this.emitValidity(); - break; - } - } - } - - /** - * réception d'un événement de ValueListComponent - */ - private onListChanged(event: any) { - if (this._initCompleted) { - switch (event.action) { - case "model": - this.validateAll(); - break; - - case "valid": - this._validList = event.value; - this.emitValidity(); - break; - } - } - } - - private get uitextValeurMini() { - return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMINI"); - } - - private get uitextValeurMaxi() { - return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMAXI"); - } + @Input() + public param: NgParameter; - private get uitextPasVariation() { - return this.intlService.localizeText("INFO_PARAMFIELD_PASVARIATION"); - } + @Input() + public title: string; - /** - * true si mode "liste de valeurs" - */ - public get isList(): boolean { - return this.param.valueMode === ParamValueMode.LISTE; - } - /** - * true si mode "lié" - */ - public get isLink(): boolean { - return this.param.valueMode === ParamValueMode.LINK; - } + constructor( + private editValuesDialog: MatDialog + ) { } - /** - * true si mode "min/max/pas" - */ - public get isMinMax(): boolean { + public get isMinMax() { return this.param.valueMode === ParamValueMode.MINMAX; } - /** - * valeur courante affichée dans le select min-max/list - */ - public get currentModeSelectLabel(): string { - return ParamValueMode[this.param.valueMode]; + public get isListe() { + return this.param.valueMode === ParamValueMode.LISTE; } - /** - * réception d'un événement du menu "min/max/liste" - */ - public onSelectValueMode(event: any) { - const next = event.target.value; - - switch (next) { - // on a sélectionné "min/max" ? - case ParamValueMode.MINMAX: - this._doInitMinmax = true; - break; - - // on a sélectionné "liste" ? - case ParamValueMode.LISTE: - this._doInitList = true; - break; - - default: - throw new Error("valeur " + next + " de ParamValueMode non prise en charge"); + public get infoText() { + let text = "(à traduire) "; + if (this.isMinMax) { + text += "min: " + this.param.minValue + ", max: " + this.param.maxValue + ", step: " + this.param.stepValue; + } else if (this.isListe) { + const vals = this.param.valueList || []; + console.log("VALS", vals, this.param.valueList); + text += "values: " + vals.slice(0, 20).join(";"); } - - this.param.valueMode = next; + return text; } - public get valueModes() { - return this._valueModes; - } - - /** - * appelé quand les @Input changent - */ - ngOnChanges() { - if (this.isMinMax) { - this._doInitMinmax = true; - } else { - this._doInitList = true; - } + public openDialog() { + // modification de la valeur initiale, sans avoir à remettre le mode de + // paramètre sur "fixé" + this.editValuesDialog.open( + DialogEditParamValuesComponent, + { + data: { + param: this.param + } + } + ); } - ngAfterViewChecked() { - super.ngAfterViewChecked(); - this.initMinMaxStep(); - this.initList(); + public ngAfterViewInit() { + // use Promise trick to introduce a pseudo-timeout and avoid ExpressionChangedAfterItHasBeenCheckedError + Promise.resolve().then(() => { + // open dialog when switching to this mode, to ease the setup + this.openDialog(); + }); } } diff --git a/src/app/components/param-values/value-list.component.ts b/src/app/components/param-values/value-list.component.ts deleted file mode 100644 index fa2fd379e9008245a8a5058d98b8d2cf0cfc0e51..0000000000000000000000000000000000000000 --- a/src/app/components/param-values/value-list.component.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { Component, Input, ChangeDetectorRef } from "@angular/core"; - -import { GenericInputComponent } from "../generic-input/generic-input.component"; -import { I18nService } from "../../services/internationalisation/internationalisation.service"; -import { NgParameter } from "../../formulaire/ngparam"; -import { Message } from "jalhyd"; - -@Component({ - selector: "value-list", - templateUrl: "../generic-input/generic-input.component.html" -}) -export class ValueListComponent extends GenericInputComponent { - constructor(private intlService: I18nService, cdRef: ChangeDetectorRef) { - super(cdRef); - } - - /** - * paramètre géré - */ - private get _param(): NgParameter { - return this._model; - } - - protected getModelValue(): any { - if (! this._param) { - return this._param.valueList; - } - } - - protected setModelValue(sender: any, l: any) { - if (typeof (l) === "number") { - this._param.valueList = []; - this._param.valueList.push(l); - } else { - this._param.valueList = l; - } - } - - protected validateModelValue(v: any): { isValid: boolean, message: string } { - let msg; - let valid = false; - - if (v instanceof Array) { - valid = true; - try { - this._param.checkList(v); - } catch (ex) { - valid = false; - if (ex instanceof Message) { - msg = this.intlService.localizeMessage(ex); - } else { - msg = "invalid value"; - } - } - } else { - msg = "Veuillez entrer une liste de nombres"; - } - - return { isValid: valid, message: msg }; - } - - protected modelToUI(v: any): string { - let res = ""; - if (v !== undefined && v !== null) { - for (const e of v) { - if (res !== "") { - res += ";"; - } - res += String(e); - } - } - return res; - } - - protected validateUIValue(ui: string): { isValid: boolean, message: string } { - let valid = false; - let msg: string; - - const tmp: string[] = ui.split(";"); - let res = true; - for (const v of tmp) { - const isnum = v !== "" && (+v === +v); - res = res && isnum; - if (!res) { - break; - } - } - - if (!res) { - msg = "Veuillez entrer une liste de nombres"; - } else { - valid = true; - } - - return { isValid: valid, message: msg }; - } - - protected uiToModel(ui: string) { - const tmp: string[] = ui.split(";"); - const res = []; - for (const v of tmp) { - res.push(+v); - } - return res; - } -} diff --git a/src/app/components/result-element/horizontal-result-element.component.html b/src/app/components/result-element/horizontal-result-element.component.html index 18f7cf7daa2f166d843325843431db5a18969041..41b4809b41b239fb51ff04a159ababa87d42c722 100644 --- a/src/app/components/result-element/horizontal-result-element.component.html +++ b/src/app/components/result-element/horizontal-result-element.component.html @@ -21,10 +21,11 @@ --> <!-- icône en cas d'erreur --> -<mat-icon *ngIf="hasError" style="color:red" aria-hidden="true" [mdbTooltip]="popTemplate" [isDisabled]="tooltipDisabled">warning</mat-icon> +<mat-icon *ngIf="hasError" style="color:red" aria-hidden="true" [isDisabled]="tooltipDisabled">warning</mat-icon> <!-- valeur --> -<span *ngIf="!hasError" [mdbTooltip]="popTemplate" [isDisabled]="tooltipDisabled"> +<!-- [mdbTooltip]="popTemplate" --> +<span *ngIf="!hasError" [isDisabled]="tooltipDisabled"> {{ resultValue }} </span> diff --git a/src/app/components/result-element/vertical-result-element.component.html b/src/app/components/result-element/vertical-result-element.component.html index 48886d327307acf4c2ce34fd9387350eb6c3901b..fa199fa1da492104e98b91bd1de8f8395dbd25fa 100644 --- a/src/app/components/result-element/vertical-result-element.component.html +++ b/src/app/components/result-element/vertical-result-element.component.html @@ -7,7 +7,8 @@ {{ resultLabel }} </td> -<td *ngIf="hasValue||hasError" [mdbTooltip]="popTemplate" [isDisabled]="tooltipDisabled" class="value2"> +<!-- [mdbTooltip]="popTemplate" --> +<td *ngIf="hasValue||hasError" [isDisabled]="tooltipDisabled" class="value2"> <!-- icône en cas d'erreur --> <mat-icon *ngIf="hasError" style="color:red" aria-hidden="true">warning</mat-icon> diff --git a/src/app/components/results-graph/graph-type.component.ts b/src/app/components/results-graph/graph-type.component.ts index 5ccfbf764b89e8dec1ff38b08a42e52380908f52..c3d41ebd9265b96f9183f1a7edf26ab6a90b85cc 100644 --- a/src/app/components/results-graph/graph-type.component.ts +++ b/src/app/components/results-graph/graph-type.component.ts @@ -1,23 +1,21 @@ import { Component } from "@angular/core"; - import { Observable, IObservable, Observer } from "jalhyd"; - -import { GenericSelectComponent } from "../generic-select/generic-select.component"; import { GraphType } from "../../results/var-results"; @Component({ selector: "graph-type", templateUrl: "../generic-select/generic-select.component.html" }) -export class GraphTypeSelectComponent extends GenericSelectComponent<GraphType> implements IObservable { +export class GraphTypeSelectComponent implements IObservable { private _entries: GraphType[] = [GraphType.Histogram, GraphType.Scatter]; private _entriesLabels: string[] = ["Histogramme", "XY"]; private _selected: GraphType; private _observable: Observable; + public label = "Type de graphe (à traduire)"; + constructor() { - super(); this._observable = new Observable(); } diff --git a/src/app/components/results-graph/results-graph.component.html b/src/app/components/results-graph/results-graph.component.html index 73ad69b83bac77af7b831eb1196232bc2248ad29..9f0dc91e235577fd23ee2138e13f465f8b40409c 100644 --- a/src/app/components/results-graph/results-graph.component.html +++ b/src/app/components/results-graph/results-graph.component.html @@ -1,12 +1,4 @@ -<div class="row"> - <div class="col-12"> - <chart [type]="graph_type" [data]="graph_data" [options]="graph_options"> - </chart> - </div> -</div> +<chart [type]="graph_type" [data]="graph_data" [options]="graph_options"> +</chart> -<div class="row"> - <div class="col-4 mx-auto"> - <graph-type></graph-type> - </div> -</div> \ No newline at end of file +<graph-type></graph-type> diff --git a/src/app/components/select-field-line/select-field-line.component.html b/src/app/components/select-field-line/select-field-line.component.html deleted file mode 100644 index c298402bd3203e34793e85c773167d2c1dc9aa19..0000000000000000000000000000000000000000 --- a/src/app/components/select-field-line/select-field-line.component.html +++ /dev/null @@ -1,16 +0,0 @@ -<div class="row"> - <!-- titre --> - <div class="col-12 col-sm-3"> - {{ label }} - </div> - - <!-- liste déroulante --> - <div class="btn-group col-12 col-sm-9" dropdown (click)="onSelect($event)"> - <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius> - {{ currentLabel }} - </button> - <div class="dropdown-menu"> - <a class="dropdown-item" *ngFor="let e of entries" [value]=e>{{ entryLabel(e) }}</a> - </div> - </div> -</div> \ No newline at end of file diff --git a/src/app/components/select-field-line/select-field-line.component.scss b/src/app/components/select-field-line/select-field-line.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..f1fe15c857bdfac181624823629e6c0c4034ce4b --- /dev/null +++ b/src/app/components/select-field-line/select-field-line.component.scss @@ -0,0 +1,14 @@ +mat-form-field { + width: 100%; + + mat-select { + + ::ng-deep .mat-select-value { + > span { + > span { + line-height: 1.3em; + } + } + } + } +} diff --git a/src/app/components/select-field-line/select-field-line.component.ts b/src/app/components/select-field-line/select-field-line.component.ts index 59f111c2364ca042154a310ad9d4809bbd74271d..a7840110a5aec8492dcacf86f4fd16775fc2c8b0 100644 --- a/src/app/components/select-field-line/select-field-line.component.ts +++ b/src/app/components/select-field-line/select-field-line.component.ts @@ -2,13 +2,16 @@ import { Component, Input } from "@angular/core"; import { SelectField } from "../../formulaire/select-field"; import { SelectEntry } from "../../formulaire/select-entry"; -import { GenericSelectComponent } from "../generic-select/generic-select.component"; @Component({ selector: "select-field-line", - templateUrl: "./select-field-line.component.html" + // templateUrl: "./select-field-line.component.html", + templateUrl: "../generic-select/generic-select.component.html", + styleUrls: [ + "./select-field-line.component.scss" + ] }) -export class SelectFieldLineComponent extends GenericSelectComponent<SelectEntry> { +export class SelectFieldLineComponent { @Input() private _select: SelectField; diff --git a/src/app/directives/flex-xxs.directive.ts b/src/app/directives/flex-xxs.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4df90248cfa5b2c76f0b77b7d804d7e10fd217e --- /dev/null +++ b/src/app/directives/flex-xxs.directive.ts @@ -0,0 +1,65 @@ +import { Directive } from "@angular/core"; +import { BREAKPOINT, ShowHideDirective, FlexDirective } from "@angular/flex-layout"; + +const XXS_BREAKPOINTS = [ + { + alias: "xxs", + mediaQuery: "screen and (max-width: 479px)", + overlapping: false + }, + { + alias: "xs", // redéfinition + mediaQuery: "screen and (min-width: 480px) screen and (max-width: 599px)", + overlapping: false + }, + { + alias: "gt-xxs", + mediaQuery: "screen and (min-width: 480px)", + overlapping: false + }, + { + alias: "lt-xs", + mediaQuery: "screen and (max-width: 479px)", + overlapping: false + } +]; + +export const CustomBreakPointsProvider = { + provide: BREAKPOINT, + useValue: XXS_BREAKPOINTS, + multi: true +}; + +const inputsXxs = [ "fxHide.xxs" ]; +const inputsGtXxs = [ "fxHide.gt-xxs" ]; +const inputsLtXs = [ "fxHide.lt-xs" ]; + +@Directive({ + // tslint:disable-next-line:directive-selector + selector: `[fxHide.xxs]`, + // tslint:disable-next-line:use-input-property-decorator + inputs: inputsXxs +}) +export class FlexXxsShowHideDirective extends ShowHideDirective { + protected inputs = inputsXxs; +} + +@Directive({ + // tslint:disable-next-line:directive-selector + selector: `[fxHide.gt-xxs]`, + // tslint:disable-next-line:use-input-property-decorator + inputs: inputsGtXxs +}) +export class FlexGtXxsShowHideDirective extends ShowHideDirective { + protected inputs = inputsGtXxs; +} + +@Directive({ + // tslint:disable-next-line:directive-selector + selector: `[fxHide.lt-xs]`, + // tslint:disable-next-line:use-input-property-decorator + inputs: inputsLtXs +}) +export class FlexLtXsShowHideDirective extends ShowHideDirective { + protected inputs = inputsLtXs; +} diff --git a/src/app/formulaire/definition/concrete/form-regime-uniforme.ts b/src/app/formulaire/definition/concrete/form-regime-uniforme.ts index b7549d6764dfdb11b89f4b6e65f90b4871981aec..63ed5d5a8249cba95173d3ce91ed355774f7accf 100644 --- a/src/app/formulaire/definition/concrete/form-regime-uniforme.ts +++ b/src/app/formulaire/definition/concrete/form-regime-uniforme.ts @@ -1,5 +1,5 @@ import { FormDefFixedVar } from "../form-def-fixedvar"; -import { CalculatorType, ComputeNodeType, IObservable, Observer } from "jalhyd"; +import { IObservable, Observer } from "jalhyd"; import { FormResultFixedVar } from "../form-result-fixedvar"; import { FormulaireDefinition } from "../form-definition"; import { FormDefSection } from "../form-def-section"; diff --git a/src/app/formulaire/definition/form-def-fixedvar.ts b/src/app/formulaire/definition/form-def-fixedvar.ts index 9e72537aad47170faa27b75b90e5a6df09bd57d5..9cd7bb4d1b13ecda0c573612e80d9220332778de 100644 --- a/src/app/formulaire/definition/form-def-fixedvar.ts +++ b/src/app/formulaire/definition/form-def-fixedvar.ts @@ -143,6 +143,7 @@ export class FormDefFixedVar { for (const p of this._formBase.allFormElements) { if (p instanceof NgParameter) { + // change all radio button groups except the one that sent the event if (p.radioConfig === ParamRadioConfig.CAL && p.radioState === ParamRadioConfig.FIX && p !== sourceParam) { newCal = p; break; @@ -152,7 +153,8 @@ export class FormDefFixedVar { break; } } - + // if the current calculated parameter was set to another mode, set a new param + // to calculated mode (there must always be at least one) if (newCal) { newCal.valueMode = ParamValueMode.CALCUL; } @@ -173,9 +175,6 @@ export class FormDefFixedVar { public onRadioClick(info: any) { const param: NgParameter = info.param; // paramètre source de l'événement radio const old: ParamValueMode = info.oldValueMode; // ancien état (radio) - - // this.logParams(); this.resetRadiosAndResults(param, old); - // this.logParams(); } } diff --git a/src/app/formulaire/ngparam.ts b/src/app/formulaire/ngparam.ts index 50125790a4fd68293ae9ae70ebc2d952eb698f0b..ad7caff505426de3180a291709579acf25df845b 100644 --- a/src/app/formulaire/ngparam.ts +++ b/src/app/formulaire/ngparam.ts @@ -54,7 +54,7 @@ export class NgParameter extends InputField implements Observer { this._confId = id; } - public get _paramValues() { // @TODO remettre en private ! (debug) + private get _paramValues() { return this._paramDef.paramValues; } @@ -148,6 +148,17 @@ export class NgParameter extends InputField implements Observer { return this._paramDef.isValid; } + public get title(): string { + let t = ""; + if (this.label !== undefined) { + t = this.label; + } + if (this.unit !== undefined && this.unit !== "") { + t = t + " (" + this.unit + ")"; + } + return t; + } + public get valuesIterator(): INumberIterator { return this._paramDef.valuesIterator; } diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json index 3ba6ffc7113404979bf15a9cbe2ca07f3ce56f68..6f454dc41fef87ba73244198a371354a064b02ce 100644 --- a/src/locale/messages.en.json +++ b/src/locale/messages.en.json @@ -31,6 +31,7 @@ "ERROR_SECTION_NON_CONVERGENCE_NEWTON_HTOR": "Non convergence of the calculation of the corresponding height (Newton's method) for the calculation of the supercritical depth", "ERROR_SECTION_PENTE_NEG_NULLE_HNORMALE_INF": "The slope is negative or zero, the normal depth is infinite", "ERROR_STRUCTURE_Q_TROP_ELEVE": "The flow passing through the other devices is too high: the requested parameter is not calculable.", + "INFO_CALCULATOR_CALC_NAME": "Calculator name", "INFO_CALCULATOR_CALCULER": "Compute", "INFO_CALCULATOR_PARAMFIXES": "Fixed parameters", "INFO_CALCULATOR_VALEURS": "Values", @@ -123,6 +124,7 @@ "INFO_OPTION_NO": "No", "INFO_OPTION_YES": "Yes", "INFO_OPTION_CANCEL": "Cancel", + "INFO_OPTION_CLOSE": "Close", "INFO_OPTION_LOAD": "Load", "INFO_OPTION_SAVE": "Save", "INFO_OPTION_ALL": "All", @@ -137,8 +139,8 @@ "INFO_PARAMFIELD_PARAMFIXE": "Fixed", "INFO_PARAMFIELD_PARAMLIE": "Link", "INFO_PARAMFIELD_PARAMVARIER": "Vary", - "INFO_PARAMFIELD_PASVARIATION": "with a variation step of:", - "INFO_PARAMFIELD_VALEURMAXI": "to maximum value", + "INFO_PARAMFIELD_PASVARIATION": "With a variation step of", + "INFO_PARAMFIELD_VALEURMAXI": "To maximum value", "INFO_PARAMFIELD_VALEURMINI": "From minimum value", "INFO_REGIMEUNIFORME_TITRE": "Uniform flow calculation", "INFO_REMOUSRESULTS_ABSCISSE": "Abscissa (m)", diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json index e5ad681f1a67e4384abbf1346d3c0801f13b1f15..490f83bec45c5d2ae0473c3a22b63ee380b41860 100644 --- a/src/locale/messages.fr.json +++ b/src/locale/messages.fr.json @@ -31,6 +31,7 @@ "ERROR_SECTION_NON_CONVERGENCE_NEWTON_HTOR": "Non convergence du calcul de la hauteur correspondante (Méthode de Newton) pour le calcul de la hauteur torrentielle", "ERROR_SECTION_PENTE_NEG_NULLE_HNORMALE_INF": "La pente est négative ou nulle, la hauteur normale est infinie", "ERROR_STRUCTURE_Q_TROP_ELEVE": "Le débit passant par les autres ouvrages est trop élevé: le paramètre demandé n'est pas calculable.", + "INFO_CALCULATOR_CALC_NAME": "Nom du module de calcul", "INFO_CALCULATOR_CALCULER": "Calculer", "INFO_CALCULATOR_PARAMFIXES": "Paramètres fixés", "INFO_CALCULATOR_VALEURS": "Valeurs", @@ -123,6 +124,7 @@ "INFO_OPTION_NO": "Non", "INFO_OPTION_YES": "Oui", "INFO_OPTION_CANCEL": "Annuler", + "INFO_OPTION_CLOSE": "Fermer", "INFO_OPTION_LOAD": "Charger", "INFO_OPTION_SAVE": "Enregistrer", "INFO_OPTION_ALL": "Tous", @@ -137,8 +139,8 @@ "INFO_PARAMFIELD_PARAMFIXE": "fixé", "INFO_PARAMFIELD_PARAMLIE": "lié", "INFO_PARAMFIELD_PARAMVARIER": "varier", - "INFO_PARAMFIELD_PASVARIATION": "avec un pas de :", - "INFO_PARAMFIELD_VALEURMAXI": "à la valeur maximum", + "INFO_PARAMFIELD_PASVARIATION": "Avec un pas de", + "INFO_PARAMFIELD_VALEURMAXI": "À la valeur maximum", "INFO_PARAMFIELD_VALEURMINI": "De la valeur minimum", "INFO_REGIMEUNIFORME_TITRE": "Régime uniforme", "INFO_REMOUSRESULTS_ABSCISSE": "Abscisse (m)", diff --git a/src/styles.scss b/src/styles.scss index b78e187721fd188a7d3ee9034a13a361bf323a73..c7f1c492cd38e5457a1c5bf66b4dcb18884d8f8d 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -33,3 +33,13 @@ h1 { font-weight: 300; line-height: 1.2; } + +// debug +field-set { + margin-bottom: 2em; +} + +// hide elements having "hidden" attribute even if they have explicit "display:" property +[hidden] { + display: none !important; +} diff --git a/src/theme.scss b/src/theme.scss index fddf50617aa7e286ddee09ffb366e4d936cd0129..3ff27ba26eb9f75fb36ca08593d3c606d7e87e66 100644 --- a/src/theme.scss +++ b/src/theme.scss @@ -182,6 +182,7 @@ $primary: map-get($nghyd-theme, primary); $accent: map-get($nghyd-theme, accent); $warn: map-get($nghyd-theme, warn); +// convenience classes (functions mat-* cannot be used outside of this file) .color-primary { color: mat-color($primary); } @@ -194,9 +195,24 @@ $warn: map-get($nghyd-theme, warn); .bg-accent { background-color: mat-color($accent); } +.bg-accent-light { + background-color: mat-color($accent, 300); +} .color-warn { color: mat-color($warn); } .bg-warn { background-color: mat-color($warn); } + +// make toggle buttons more visible +/* .mat-button-toggle { + background-color: mat-color($primary); + color: mat-color($primary, default-contrast); +} + */ + +.mat-button-toggle-checked { + background-color: mat-color($accent); + color: mat-color($accent, default-contrast) !important; +} diff --git a/tsconfig.json b/tsconfig.json index cb174fd8a71047dfd9d30bce1acb17757e54f155..f6d913f28b3fb0497654c6b9cc9f5c726ac0ad09 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,7 +20,6 @@ "baseUrl": "./" }, "include": [ - "node_modules/angular-bootstrap-md/**/*.ts", "src/**/*.ts" ] } \ No newline at end of file