2021-10-21 17:09:57 +02:00
const { compare } = require ( 'natural-orderby' ) ;
const errors = require ( "./errors.js" ) ;
2021-10-18 10:37:44 +02:00
import { DOMElement , Selectors } from "./interfaces" ;
2021-10-25 10:53:54 +02:00
import { FreeDatas2HTML } from "./FreeDatas2HTML" ;
2021-09-17 18:02:45 +02:00
export class Selector implements Selectors
{
2021-10-21 17:09:57 +02:00
private _converter : FreeDatas2HTML ;
private _datasFieldNb : number ;
private _datasViewElt : DOMElement = { id : "" , eltDOM : undefined } ;
2022-01-13 17:25:06 +01:00
private _selectedValues : number [ ] = [ ] ;
2021-10-21 17:09:57 +02:00
private _separator : string | undefined ;
private _values : string [ ] = [ ] ;
private _name : string = "" ;
2022-01-13 17:25:06 +01:00
public isMultiple : boolean = false ; // permet à l'utilisateur de sélectionner plusieurs valeurs dans la liste.
2021-09-20 11:09:30 +02:00
2021-12-15 16:10:29 +01:00
// Injection de la classe principale, mais uniquement si des données ont été importées.
// Le champ duquel le sélecteur tire ses données doit exister.
2021-10-21 17:09:57 +02:00
constructor ( converter : FreeDatas2HTML , datasFieldNb : number , elt : DOMElement , separator? : string )
2021-09-17 18:02:45 +02:00
{
2021-10-26 18:02:43 +02:00
if ( converter . fields . length === 0 || converter . datas . length === 0 )
2021-10-21 17:09:57 +02:00
throw new Error ( errors . filterNeedDatas ) ;
2021-09-22 17:12:00 +02:00
else if ( ! converter . checkFieldExist ( Number ( datasFieldNb ) ) )
throw new Error ( errors . selectorFieldNotFound ) ;
2021-09-17 18:02:45 +02:00
else
2021-09-22 17:12:00 +02:00
{
2021-10-21 17:09:57 +02:00
this . _datasViewElt = FreeDatas2HTML . checkInDOMById ( elt ) ;
2021-09-17 18:02:45 +02:00
this . _converter = converter ;
2021-09-22 17:12:00 +02:00
this . _datasFieldNb = datasFieldNb ;
2021-10-21 17:09:57 +02:00
// Pas de trim(), car l'espace peut être le séparateur :
if ( separator !== undefined && separator !== "" )
this . _separator = separator ;
2022-01-13 17:25:06 +01:00
this . _name = this . _converter . fields [ this . _datasFieldNb ] ;
this . setValues ( ) ;
2021-09-22 17:12:00 +02:00
}
2021-09-17 18:02:45 +02:00
}
2021-09-22 17:12:00 +02:00
2021-10-21 17:09:57 +02:00
get converter ( ) : FreeDatas2HTML
2021-09-17 18:02:45 +02:00
{
2021-10-21 17:09:57 +02:00
return this . _converter ;
2021-09-17 18:02:45 +02:00
}
get datasViewElt ( ) : DOMElement
{
return this . _datasViewElt ;
}
2021-09-22 17:12:00 +02:00
get datasFieldNb ( ) : number
2021-09-17 18:02:45 +02:00
{
return this . _datasFieldNb ;
}
2021-09-22 17:12:00 +02:00
2021-10-21 17:09:57 +02:00
get name ( ) : string
{
return this . _name ;
}
2022-01-13 17:25:06 +01:00
get selectedValues ( ) : number [ ]
2021-10-21 17:09:57 +02:00
{
2022-01-13 17:25:06 +01:00
return this . _selectedValues ;
2021-10-21 17:09:57 +02:00
}
2021-09-22 17:12:00 +02:00
get separator ( ) : string | undefined
2021-09-17 18:02:45 +02:00
{
2021-09-22 17:12:00 +02:00
return this . _separator ;
2021-09-17 18:02:45 +02:00
}
2021-10-21 17:09:57 +02:00
get values ( ) : string [ ]
2021-09-17 18:02:45 +02:00
{
2021-10-21 17:09:57 +02:00
return this . _values ;
}
2022-01-13 17:25:06 +01:00
// Dédoublonnage et classement des valeurs disponibles pour le champ du sélecteur
private setValues ( ) : void
2021-10-21 17:09:57 +02:00
{
2022-01-13 17:25:06 +01:00
for ( const row of this . _converter . datas )
2021-09-17 18:02:45 +02:00
{
2021-10-21 17:09:57 +02:00
let checkedValue ;
if ( this . _separator === undefined )
2021-09-17 18:02:45 +02:00
{
2021-12-15 16:10:29 +01:00
checkedValue = row [ this . _name ] . trim ( ) ; // trim() évite des problèmes de classement des éléments du SELECT.
2021-10-21 17:09:57 +02:00
if ( checkedValue !== "" && this . _values . indexOf ( checkedValue ) === - 1 )
this . _values . push ( checkedValue ) ;
2021-09-17 18:02:45 +02:00
}
2021-10-21 17:09:57 +02:00
else
2021-09-17 18:02:45 +02:00
{
2021-10-21 17:09:57 +02:00
let checkedValues = row [ this . _name ] . split ( this . _separator ) ;
2022-01-13 17:25:06 +01:00
for ( const value of checkedValues )
2021-10-21 17:09:57 +02:00
{
checkedValue = value . trim ( ) ;
if ( checkedValue !== "" && this . _values . indexOf ( checkedValue ) === - 1 )
this . _values . push ( checkedValue ) ;
}
2021-09-17 18:02:45 +02:00
}
}
2022-01-13 17:25:06 +01:00
2021-12-15 16:10:29 +01:00
if ( this . _values . length === 0 ) // possible, si uniquement des valeurs vides pour ce champ.
2021-10-21 17:09:57 +02:00
throw new Error ( errors . selectorFieldIsEmpty ) ;
2021-09-17 18:02:45 +02:00
else
2021-10-21 17:09:57 +02:00
{
// Classement des données à l'aide (ou non) d'une fonction spécifique :
if ( this . _converter . getSortingFunctionForField ( this . _datasFieldNb ) !== undefined )
this . _values . sort ( this . _converter . getSortingFunctionForField ( this . _datasFieldNb ) ! . sort ) ; // sans le "!" : TS2532: Object is possibly 'undefined' ???
else
this . _values . sort ( compare ( ) ) ;
2022-01-13 17:25:06 +01:00
}
}
2021-10-21 17:09:57 +02:00
2022-01-13 17:25:06 +01:00
// Création du <select> dans le DOM correspondant au filtre
public filter2HTML ( label :string = "" ) : void
{
label = ( label === "" ) ? this . _name : label ;
const multipleAttr = ( this . isMultiple ) ? " multiple" : "" ;
let selectorsHTML = "<label for='freeDatas2HTML_" + this . _datasViewElt . id + "'>" + label + " :</label><select name='freeDatas2HTML_" + this . _datasViewElt . id + "' id='freeDatas2HTML_" + this . _datasViewElt . id + "'" + multipleAttr + "><option value='0'>----</option>" ; // l'option zéro permet d'actualiser l'affichage en ignorant ce filtre.
for ( let i = 0 ; i < this . _values . length ; i ++ )
selectorsHTML += "<option value='" + ( i + 1 ) + "'>" + this . _values [ i ] + "</option>" ;
selectorsHTML += "</select>" ;
this . _datasViewElt . eltDOM ! . innerHTML = selectorsHTML ; // "!", car l'existence de "eltDOM" est testé par le constructeur.
// Actualisation de l'affichage lorsqu'une valeur est sélectionnée :
const selectElement = document . getElementById ( "freeDatas2HTML_" + this . _datasViewElt . id ) as HTMLSelectElement , mySelector = this ;
selectElement . addEventListener ( "change" , function ( e )
{
mySelector . _selectedValues = [ ] ;
if ( mySelector . isMultiple )
{
for ( let i = 0 ; i < selectElement . selectedOptions . length ; i ++ )
{
const selectedValue = parseInt ( selectElement . selectedOptions [ i ] . value , 10 ) ;
if ( selectedValue === 0 ) // = annulation de ce filtre
{
mySelector . _selectedValues = [ ] ;
break ;
}
else
mySelector . _selectedValues . push ( selectedValue - 1 ) ;
}
}
else
{
let selectedValue = parseInt ( selectElement . value , 10 ) ;
if ( selectedValue === 0 ) // = annulation de ce filtre
mySelector . _selectedValues = [ ] ;
2021-10-21 17:09:57 +02:00
else
2022-01-13 17:25:06 +01:00
mySelector . _selectedValues [ 0 ] = selectedValue - 1 ;
}
mySelector . _converter . refreshView ( ) ;
} ) ;
2021-09-17 18:02:45 +02:00
}
2022-01-13 17:25:06 +01:00
2021-10-21 17:09:57 +02:00
public dataIsOk ( data : { [ index : string ] : string } ) : boolean
2021-09-20 11:09:30 +02:00
{
2022-01-13 17:25:06 +01:00
const checkIsValid = ( selector : Selector , data : { [ index : string ] : string } , checkedValue :string ) : boolean = >
2021-09-17 18:02:45 +02:00
{
2022-01-13 17:25:06 +01:00
if ( selector . _separator === undefined )
{
if ( data [ selector . _name ] . trim ( ) !== checkedValue )
return false ;
else
return true ;
}
2021-09-17 18:02:45 +02:00
else
{
2022-01-13 17:25:06 +01:00
let find = false ;
let checkedValues = data [ selector . _name ] . split ( selector . _separator ) ;
for ( let value of checkedValues )
2021-09-17 18:02:45 +02:00
{
2022-01-13 17:25:06 +01:00
if ( value . trim ( ) === checkedValue )
{
find = true ;
break ;
}
2021-09-17 18:02:45 +02:00
}
2022-01-13 17:25:06 +01:00
return find ;
2021-09-17 18:02:45 +02:00
}
2022-01-13 17:25:06 +01:00
} ;
// Pas de valeur sélectionnée = pas de filtre sur ce champ, donc tout passe :
if ( this . _selectedValues . length === 0 )
return true ;
// Un enregistrement n'ayant pas le champ du filtre sera refusé :
if ( data [ this . _name ] === undefined )
return false ;
// Si plusieurs options sont sélectionnées dans une liste multiple,
// il suffit qu'une soit trouvée pour que l'enregistrement soit valide.
let find = false ;
for ( const value of this . _selectedValues )
{
if ( this . _values [ value ] === undefined ) // théoriquement impossible, mais cela vient du client...
throw new Error ( errors . selectorSelectedIndexNotFound ) ;
find = checkIsValid ( this , data , this . _values [ value ] ) ;
if ( find )
break ;
2021-09-17 18:02:45 +02:00
}
2022-01-13 17:25:06 +01:00
return find ;
2021-09-17 18:02:45 +02:00
}
}