An error occurred while loading the file. Please try again.
-
Mathias Chouet authoredcfe75a30
import { Component, Input, Output, EventEmitter, OnInit } from "@angular/core";
import {
Pab,
Session,
PabCloisons,
Props,
CalculatorType,
ParallelStructure,
LoiDebit,
Cloisons,
Nub,
Structure
} from "jalhyd";
import { I18nService } from "../../services/internationalisation/internationalisation.service";
import { PabTable } from "../../formulaire/pab-table";
/**
* The big editable data grid for calculator type "Pab" (component)
*/
@Component({
selector: "pab-table",
templateUrl: "./pab-table.component.html",
styleUrls: [
"./pab-table.component.scss"
]
})
export class PabTableComponent implements /* DoCheck, AfterViewInit, */ OnInit {
@Input()
private pabTable: PabTable;
/** flag de validité des FieldSet enfants */
private _isValid = false;
/** événément de changement de validité */
@Output()
private validChange = new EventEmitter();
/** événément de changement de valeur d'un input */
@Output()
private inputChange = new EventEmitter();
/** underlying Pab, binded to the rows */
private model: Pab;
/** general headers above the columns */
public headers: any[];
/** columns headers description */
public cols: any[];
/** data binded to the table */
public rows: any[];
/** number of children to add when clicking "add" or "clone" button */
public childrenToAdd = 1;
/** items currently selected */
private selectedItems: any[];
/** used for shift+click implementation */
private latestClickedCell: any;
public constructor(
private i18nService: I18nService
) {
this.selectedItems = [];
}
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
public get title(): string {
return this.i18nService.localizeText("INFO_PAB_TABLE");
}
public get isValid() {
return this._isValid;
}
/** returns true if the cell has an underlying model (ie. is editable) */
public hasModel(cell: any): boolean {
return (cell !== undefined && cell.model !== undefined);
}
/** returns true if the cell is an editable number */
public isNumberInput(cell: any): boolean {
return this.hasModel(cell) && ! this.isSelect(cell);
}
/** returns true if the cell is a select box @TODO rename */
public isSelect(cell: any): boolean {
return this.hasModel(cell) && (cell.options !== undefined);
}
/** value to display in a cell, if it is not editable */
public cellValue(cell: any) {
if (cell === undefined) {
return "";
} else {
if (this.hasModel(cell)) {
return cell.model;
} else {
return cell.value;
}
}
}
public rowSpan(cell: any) {
if (cell !== undefined && cell.rowspan) {
return cell.rowspan;
}
return undefined;
}
public colSpan(cell: any) {
if (cell !== undefined && cell.colspan) {
return cell.colspan;
}
return undefined;
}
/** returns true if the cell / row has a selectable item */
public isSelectable(cellOrRow: any): boolean {
return (
cellOrRow !== undefined
&& cellOrRow.selectable
);
}
/** returns true if the cell / row has a selectableColumn item */
public isSelectableByColumn(cellOrRow: any): boolean {
return (
cellOrRow !== undefined
&& cellOrRow.selectableColumn
);
}
/**
* - checks if the cell / row has a selectable item, that is currently
* selected
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
* - if cell / row has a selectableColumn attribute, also checks if
* this column is selected
*
* returns true if at least one criterion is met
*/
public isSelected(cellOrRow: any): boolean {
let cellSelected = false;
let columnSelected = false;
// cell
if (this.isSelectable(cellOrRow)) {
cellSelected = true;
if (Array.isArray(cellOrRow.selectable)) {
for (const elt of cellOrRow.selectable) {
cellSelected = cellSelected && this.selectedItems.includes(elt);
}
} else {
cellSelected = this.selectedItems.includes(cellOrRow.selectable);
}
}
// column
if (this.isSelectableByColumn(cellOrRow)) {
columnSelected = this.isDeviceColumnSelected(cellOrRow.selectableColumn);
}
// done
return (cellSelected || columnSelected);
}
/**
* returns true if every wall (including downwall) has its nth device
* selected (or has no nth device)
*/
public isDeviceColumnSelected(n: number): boolean {
let ok = true;
for (const c of this.model.children) {
const nthChild = c.getChildren()[n];
if (nthChild) {
ok = ok && this.selectedItems.includes(nthChild);
}
}
const nthChildDW = this.model.downWall.getChildren()[n];
if (nthChildDW) {
ok = ok && this.selectedItems.includes(nthChildDW);
}
return ok;
}
/**
* selects or unselects the clicked cell, depending on its current state
* and the modifier key held if any
*/
public toggleSelection(cell: any, $event: any) {
if (
this.isSelectable(cell)
&& ! this.hasModel(cell) // editable cells listen to the click event for edition only
) {
if ($event.shiftKey && cell !== this.latestClickedCell) { // shift + click
// @TODO interpopolate from this.latestClickedCell to this one
console.log("shift + click");
} else if (
$event.ctrlKey // ctrl + click
|| ($event.shiftKey && cell === this.latestClickedCell) // shift on same cell => equiv. of ctrl
) {
if (this.isSelected(cell)) {
// unselect this cell / these cells
if (Array.isArray(cell.selectable)) {
this.selectedItems = this.selectedItems.filter(e => ! cell.selectable.includes(e));
} else {
this.selectedItems = this.selectedItems.filter(e => e !== cell.selectable);
}
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
} else {
// add this cell / these cells to selection
if (Array.isArray(cell.selectable)) {
this.selectedItems = this.selectedItems.concat(cell.selectable);
this.selectedItems = this.selectedItems.filter(
(item, index) => this.selectedItems.indexOf(item) === index // deduplicate
);
} else {
this.selectedItems.push(cell.selectable);
}
}
} else { // just a click
if (this.isSelected(cell)) {
// select nothing
this.selectedItems = [];
} else {
// select this cell / thses cells only
if (Array.isArray(cell.selectable)) {
this.selectedItems = cell.selectable.slice(); // array copy
} else {
this.selectedItems = [ cell.selectable ];
}
}
}
this.latestClickedCell = cell;
// clean list
this.selectedItems = this.selectedItems.filter(e => e !== undefined);
// @TODO useful ?
$event.preventDefault();
$event.stopPropagation();
return false;
}
}
// prevents Firefox to display weird cell border when ctrl+clicking
public preventCtrlClickBorder($event) {
if ($event.ctrlKey) {
$event.preventDefault();
}
}
public get addManyOptionsList() {
return Array(20).fill(0).map((value, index) => index + 1);
}
// DEBUG
private dumpParams(n: Nub) {
console.log(`---- PARAMETERS FOR NUB "${n.uid}" (${n.constructor.name}) ----`);
for (const p of n.parameterIterator) {
if (p.visible) {
console.log(`> ${p.symbol} : ${p.singleValue}, ${p.currentValue}, ${p.v}`);
}
}
}
public zpouet() {
console.log("!!!------------ zpoueeeeeeeeet ------------!!!");
for (const c of this.model.children) {
this.dumpParams(c);
}
this.dumpParams(this.model.downWall);
}
// at this time @Input data is supposed to be already populated
public ngOnInit() {
// get model
this.model = this.pabTable.pab;
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
// ------------- TEST ------------------------
// empty PAB
const chLen = this.model.children.length;
for (let i = 0; i < chLen; i++) {
this.model.deleteChild(0);
}
// cloison à 1 ouvrage
const cl1 = Session.getInstance().createNub(
new Props({ calcType: CalculatorType.Cloisons })
) as Cloisons;
cl1.addChild(Session.getInstance().createNub(
new Props({ calcType: CalculatorType.Structure, loiDebit: LoiDebit.RectangularOrificeFree }), cl1
));
// PabCloison associé
const pc1 = Session.getInstance().createNub(
new Props({ calcType: CalculatorType.PabCloisons }), this.model
) as PabCloisons;
pc1.initModelCloisons(cl1);
pc1.prms.setCurrentValuesFromModel();
this.model.addChild(pc1);
this.dumpParams(pc1);
// cloison à 3 ouvrages
const cl2 = Session.getInstance().createNub(
new Props({ calcType: CalculatorType.Cloisons })
) as Cloisons;
cl2.addChild(Session.getInstance().createNub(
new Props({ calcType: CalculatorType.Structure, loiDebit: LoiDebit.GateCem88v }), cl2
));
cl2.addChild(Session.getInstance().createNub(
new Props({ calcType: CalculatorType.Structure, loiDebit: LoiDebit.RectangularOrificeFree }), cl2
));
cl2.addChild(Session.getInstance().createNub(
new Props({ calcType: CalculatorType.Structure, loiDebit: LoiDebit.RectangularOrificeSubmerged }), cl2
));
// PabCloison associé
const pc2 = Session.getInstance().createNub(
new Props({ calcType: CalculatorType.PabCloisons }), this.model
) as PabCloisons;
pc2.initModelCloisons(cl2);
pc2.prms.setCurrentValuesFromModel();
this.model.addChild(pc2);
this.dumpParams(pc2);
// cloison à 2 ouvrages
const cl3 = Session.getInstance().createNub(
new Props({ calcType: CalculatorType.Cloisons })
) as Cloisons;
cl3.addChild(Session.getInstance().createNub(
new Props({ calcType: CalculatorType.Structure, loiDebit: LoiDebit.GateCem88v }), cl3
));
cl3.addChild(Session.getInstance().createNub(
new Props({ calcType: CalculatorType.Structure, loiDebit: LoiDebit.RectangularOrificeFree }), cl3
));
// PabCloison associé
const pc3 = Session.getInstance().createNub(
new Props({ calcType: CalculatorType.PabCloisons }), this.model
) as PabCloisons;
pc3.initModelCloisons(cl3);
pc3.prms.setCurrentValuesFromModel();
this.model.addChild(pc3);
this.dumpParams(pc3);
// cloison aval à 1 ouvrage
const cloisonAval: ParallelStructure = Session.getInstance().createSessionNub(
new Props({ calcType: CalculatorType.ParallelStructure })
) as ParallelStructure;
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
cloisonAval.addChild(Session.getInstance().createNub(
new Props({ calcType: CalculatorType.Structure, loiDebit: LoiDebit.KIVI }), cloisonAval
));
this.model.setDownWall(cloisonAval.uid);
this.dumpParams(this.model.downWall);
// debug
console.log("Model in ngOnInit:", this.model);
// ------------- FIN TEST --------------------
this.buildTable();
}
/**
* Builds the editable data grid from the Pab model
*/
private buildTable() {
const maxNbDevices = this.findMaxNumberOfDevices();
// 0. build spanned headers over real columns
this.headers = [];
// 1 header for basin
let bs: any[] = this.model.children;
bs = bs.concat(this.model.downWall);
this.headers.push({
title: "Bassin",
colspan: 6,
selectable: bs
});
// 1 header for each device of the wall having the most devices (including downwall)
for (let i = 0; i < maxNbDevices; i++) {
this.headers.push({
title: "Cloison : ouvrage n°" + (i + 1),
colspan: 3,
selectable: this.model.children.map(c => c.getChildren()[i]).concat(this.model.downWall.getChildren()[i]),
selectableColumn: i
});
}
// A. build columns set
this.cols = [];
// 6 cols for basin
this.cols.push({
title: "N° de bassin",
selectable: bs
});
this.cols.push({
title: "Longueur",
selectable: bs
});
this.cols.push({
title: "Largeur",
selectable: bs
});
this.cols.push({
title: "Débit d'attrait",
selectable: bs
});
this.cols.push({
title: "Cote radier mi-bassin",
selectable: bs
});
this.cols.push({
title: "Cote radier amont paroi",
selectable: bs
});
// no col for wall type (defined by rowspan-2 header above)
// 3 cols for each device of the wall having the most devices (including downwall)
for (let i = 0; i < maxNbDevices; i++) {
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
this.cols.push({
title: "Type",
selectable: this.model.children.map(c => c.getChildren()[i]).concat(this.model.downWall.getChildren()[i]),
selectableColumn: i
});
this.cols.push({
title: "Paramètres",
selectable: this.model.children.map(c => c.getChildren()[i]).concat(this.model.downWall.getChildren()[i]),
selectableColumn: i
});
this.cols.push({
title: "Valeurs",
selectable: this.model.children.map(c => c.getChildren()[i]).concat(this.model.downWall.getChildren()[i]),
selectableColumn: i
});
}
// B. Build rows set
this.rows = [];
// B.1 many rows for each wall
let childIndex = 0;
for (const cloison of this.model.children) {
// as much rows as the greatest number of parameters among its devices
const maxNbParams = this.findMaxNumberOfDeviceParameters(cloison);
// console.log(">>> max nb params: ", maxNbParams);
for (let i = 0; i < maxNbParams; i++) {
// build device params row
const deviceParamRow = { selectable: cloison, cells: [] };
// basin number
if (i === 0) {
deviceParamRow.cells.push({
value: childIndex + 1,
rowspan: maxNbParams + 1,
class: "basin_number",
selectable: cloison
});
}
// 5 empty cells
if (i === 0) {
deviceParamRow.cells.push({
colspan: 5,
rowspan: maxNbParams,
selectable: cloison
});
}
// device param cells : 3 cells for each device
for (const ouvrage of cloison.structures) {
const nvParam = ouvrage.getNthVisibleParam(i);
// cell 1 : device type
if (i === 0) { // 1st row
deviceParamRow.cells.push({
model: ouvrage.properties.getPropValue("loiDebit"),
// options: StructureProperties.findCompatibleLoiDebit(ouvrage)
options: [ "salut", "coucou", "pouet" ],
selectable: ouvrage
});
}
// fill space
if (i === 1) {
deviceParamRow.cells.push({
rowspan: (maxNbParams - 1),
selectable: ouvrage
});
}
// cell 2 : param name
if (nvParam) {
deviceParamRow.cells.push({
value: nvParam.symbol,
selectable: ouvrage
});
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
} else {
deviceParamRow.cells.push({
selectable: ouvrage
});
}
// cell 3 : param value
if (nvParam) {
deviceParamRow.cells.push({
model: nvParam,
selectable: ouvrage
});
} else {
deviceParamRow.cells.push({
selectable: ouvrage
});
}
}
// fill horizontal space
const devDiff = (maxNbDevices - cloison.structures.length);
if (i === 0) {
for (let j = 0; j < devDiff; j++) {
deviceParamRow.cells.push({
colspan: 3,
rowspan: maxNbParams,
selectable: cloison,
selectableColumn: cloison.structures.length + j
});
}
}
// done !
this.rows.push(deviceParamRow);
}
// 1 row for the basin after the wall
const basinRow: { selectable: any, cells: any[] } = {
selectable: cloison,
cells: [
// no cell for basin number (defined by rowspan-n cell above)
{
model: cloison.prms.LB
},
{
model: cloison.prms.BB
},
{
model: cloison.prms.QA
},
{
model: cloison.prms.ZRMB
},
{
model: cloison.prms.ZRAM
}
]
};
// fill horizontal space
for (let i = 0; i < maxNbDevices; i++) {
basinRow.cells.push({
colspan: 3
});
}
// done !
this.rows.push(basinRow);
childIndex ++;
}
// B.2 many rows for downwall
// as much rows as the greatest number of parameters among its devices
const maxNbParamsDW = this.findMaxNumberOfDeviceParameters(this.model.downWall);
for (let i = 0; i < maxNbParamsDW; i++) {
// build device params row
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
const deviceParamRowDW = { selectable: this.model.downWall, cells: [] };
// basin number
if (i === 0) {
deviceParamRowDW.cells.push({
value: "Aval",
rowspan: maxNbParamsDW,
class: "basin_number",
selectable: this.model.downWall
});
}
// 5 empty cells
if (i === 0) {
deviceParamRowDW.cells.push({
colspan: 5,
rowspan: maxNbParamsDW,
selectable: this.model.downWall
});
}
// downwall device param cells : 3 cells for each device
for (const ouvrage of this.model.downWall.structures) {
const nvParam = ouvrage.getNthVisibleParam(i);
// cell 1 : device type
if (i === 0) { // 1st row
deviceParamRowDW.cells.push({
model: ouvrage.properties.getPropValue("loiDebit"),
// options: StructureProperties.findCompatibleLoiDebit(ouvrage)
options: [ "salut", "coucou", "pouet" ]
});
}
// fill space
if (i === 1) {
deviceParamRowDW.cells.push({
rowspan: (maxNbParamsDW - 1),
selectable: ouvrage
});
}
// cell 2 : param name
if (nvParam) {
deviceParamRowDW.cells.push({
value: nvParam.symbol,
selectable: ouvrage
});
} else {
deviceParamRowDW.cells.push({
selectable: ouvrage
});
}
// cell 3 : param value
if (nvParam) {
deviceParamRowDW.cells.push({
model: nvParam,
selectable: ouvrage
});
} else {
deviceParamRowDW.cells.push({
selectable: ouvrage
});
}
}
// fill horizontal space
const devDiff = (maxNbDevices - this.model.downWall.structures.length);
if (i === 0) {
for (let j = 0; j < devDiff; j++) {
deviceParamRowDW.cells.push({
colspan: 3,
rowspan: maxNbParamsDW,
selectable: this.model.downWall,
selectableColumn: this.model.downWall.structures.length + j
});
}
631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
}
// done !
this.rows.push(deviceParamRowDW);
}
}
private findMaxNumberOfDevices(): number {
let maxNbDevices = 1;
for (const w of this.model.children) {
maxNbDevices = Math.max(maxNbDevices, w.getChildren().length);
}
maxNbDevices = Math.max(maxNbDevices, this.model.downWall.getChildren().length);
return maxNbDevices;
}
private findMaxNumberOfDeviceParameters(struct: ParallelStructure): number {
let maxNbParams = 1;
for (const d of struct.getChildren()) {
let nbParams = 0;
for (const p of d.parameterIterator) {
if (p.visible) {
// console.log("(counting)", p.symbol);
nbParams ++;
}
}
// console.log(">>> child params: ", nbParams);
maxNbParams = Math.max(maxNbParams, nbParams);
}
return maxNbParams;
}
/** returns true if exactly one device is selected, and nothing else */
public get selectionIsOneDevice() {
return (
this.selectedItems.length === 1
&& this.selectedItems[0] instanceof Structure
);
}
/**
* Returns true if there is at least one selected item,
* and all selected items are devices
*/
private onlyDevicesAreSelected() {
let ok = false;
if (this.selectedItems.length > 0) {
ok = true;
for (const s of this.selectedItems) {
ok = ok && (s instanceof Structure);
}
}
return ok;
}
/**
* Returns true if there is at least one selected item,
* and all selected items are walls
*/
private onlyWallsAreSelected() {
let ok = false;
if (this.selectedItems.length > 0) {
ok = true;
for (const s of this.selectedItems) {
ok = ok && (s instanceof ParallelStructure);
}
}
return ok;
}
public get relatedEntityTitle() {
701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
let title = "";
if (this.onlyDevicesAreSelected()) {
title = "Ouvrages";
} else if (this.onlyWallsAreSelected()) {
title = "Cloisons";
}
if (title !== "") {
title += " :";
}
return title;
}
public get enableAddButton() {
return this.selectedItems.length === 1;
}
public get enableCopyButton() {
return this.selectedItems.length === 1;
}
public get enableUpButton() {
return this.selectedItems.length === 1;
}
public get enableDownButton() {
return this.selectedItems.length === 1;
}
public get enableRemoveButton() {
return this.selectedItems.length === 1;
// return this.selectedItems.length > 0; // too dangerous ? or @TODO ask confirmation ?
}
/**
* returns true if "many" objects are selected: either more than one object,
* or a wall that contains more than one device
*/
public get enableEditPabButton() {
return (
this.selectedItems.length > 0
// too restrictive ?
/* this.selectedItems.length > 1
|| (
this.selectedItems.length > 0
&& this.selectedItems[0] instanceof ParallelStructure
&& this.selectedItems[0].getChildren().length > 1
) */
);
}
public onAddClick() {
console.log("Add !!!");
}
public onCopyClick() {
console.log("Copy !!!");
}
public onMoveUpClick() {
console.log("Move up !!!");
}
public onMoveDownClick() {
console.log("Move down !!!");
}
public onRemoveClick() {
console.log("Remove !!!");
}
771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831
public showEditPab() {
console.log(">> Edit PAB !!");
}
/* public ngAfterViewInit() {
this.onFieldsetListChange();
this._fieldsetComponents.changes.subscribe(_ => this.onFieldsetListChange());
}
public ngDoCheck() {
this.updateValidity();
} */
/**
* @TODO Calcul de la validité de la Pab
*/
private updateValidity() {
this._isValid = false;
/* if (this._fieldsetComponents !== undefined) {
this._isValid = this._fieldsetComponents.reduce(
// callback
(
// accumulator (valeur précédente du résultat)
acc,
// currentValue (élément courant dans le tableau)
fieldset,
// currentIndex (indice courant dans le tableau)
currIndex,
// array (tableau parcouru)
array
) => {
return acc && fieldset.isValid;
}
// valeur initiale
, this._fieldsetComponents.length > 0);
} */
this.validChange.emit();
}
/**
* réception d'un événement de changement de valeur d'un input
*/
private onInputChange($event) {
this.inputChange.emit($event);
}
/**
* Renvoie l'événement au composant du dessus
*/
public onTabPressed(event) {
console.log("tab pressed dans le tablo !");
}
public get uitextEditPabTable() {
return "Modifier les valeurs";
}
}