2021-02-12 12:31:12 +01:00
import {
2022-02-08 10:52:11 +01:00
AfterViewInit ,
2021-03-22 16:10:18 +01:00
Component ,
ElementRef ,
EventEmitter ,
HostListener ,
2021-02-12 12:31:12 +01:00
Input ,
OnChanges ,
OnDestroy ,
OnInit ,
Output ,
SimpleChanges ,
ViewChild
} from "@angular/core" ;
2021-04-23 18:25:52 +02:00
import { AbstractControl , FormArray , FormControl , ValidatorFn } from "@angular/forms" ;
2020-11-12 18:57:32 +01:00
import { HelperFunctions } from "../../utils/HelperFunctions.class" ;
2022-03-30 19:47:47 +02:00
import { Subscription } from "rxjs" ;
2022-03-28 00:21:55 +02:00
import { EnvProperties } from "../../utils/properties/env-properties" ;
import { properties } from "../../../../environments/environment" ;
2019-12-23 16:17:35 +01:00
export interface Option {
icon? : string ,
iconClass? : string ,
value : any ,
label : string
}
2022-03-31 15:39:17 +02:00
export interface Placeholder {
label : string ,
static ? : boolean
}
2022-03-30 19:47:47 +02:00
declare var UIkit ;
2022-03-28 00:21:55 +02:00
/ * *
* WARNING ! dashboard - input selector is @deprecated , use input instead
*
* * /
2019-12-23 14:59:56 +01:00
@Component ( {
2022-03-28 00:21:55 +02:00
selector : '[dashboard-input], [input]' ,
2019-12-23 14:59:56 +01:00
template : `
2022-03-31 15:39:17 +02:00
< div class = "input-border" [ class.disabled ] = " formControl.disabled "
2022-03-30 19:47:47 +02:00
[ class . danger ] = "(formControl.invalid && (formControl.touched || searchControl?.touched)) || (searchControl?.invalid && searchControl?.touched)" >
2022-03-31 15:39:17 +02:00
< div # inputBox class = "input-wrapper" [ class.static ] = " placeholderInfo.static "
[ class . focused ] = "focused" [ class . select ] = "type === 'select'"
[ class . active ] = "(focused && type !== 'select') || formAsControl?.value || formAsArray?.length > 0 || hint"
[ ngClass ] = "inputClass" >
< div * ngIf = "!placeholderInfo.static && placeholderInfo.label" class = "placeholder" >
< label > { { placeholderInfo . label } } < sup * ngIf = "required" > * < / sup > < / label >
< / div >
< div class = "uk-flex" [ class.uk - flex - middle ] = " type ! = = ' textarea ' " >
< ng - template [ ngIf ] = " type = = = ' text ' | | type = = = ' URL ' | | type = = = ' logoURL ' " >
< input # input class = "input" [ attr.placeholder ] = " placeholderInfo.static ? placeholderInfo.label : hint "
[ formControl ] = "formAsControl" >
< / n g - t e m p l a t e >
< ng - template [ ngIf ] = " type = = = ' textarea ' " >
< textarea # textArea class = "input" [ attr.placeholder ] = " placeholderInfo.static ? placeholderInfo.label : hint "
[ rows ] = "rows" [ formControl ] = "formAsControl" > < / textarea >
< / n g - t e m p l a t e >
< ng - template [ ngIf ] = " type = = = ' select ' " >
< div class = "input" > { { getLabel ( formControl . value ) } } < / div >
< / n g - t e m p l a t e >
< ng - template [ ngIf ] = " type = = = ' autocomplete ' " >
< div class = "uk-grid uk-width-expand" uk - grid >
< div * ngIf = "formControl.value" >
< div class = "input" > { { getLabel ( formControl . value ) } } < / div >
2022-03-30 19:47:47 +02:00
< / div >
2022-03-31 15:39:17 +02:00
< div class = "uk-width-expand" >
< input [ class.uk - hidden ] = " formControl.value " [ attr.placeholder ] = " placeholderInfo.static ? placeholderInfo.label : hint "
# searchInput class = "input" [ class . search ] = "searchControl.value" [ formControl ] = "searchControl" >
2022-03-30 19:47:47 +02:00
< / div >
< / div >
2022-03-31 15:39:17 +02:00
< / n g - t e m p l a t e >
< ng - template [ ngIf ] = " type = = = ' chips ' " >
< div class = "uk-grid uk-grid-small uk-grid-row-collapse uk-width-expand" uk - grid >
< div * ngFor = "let chip of formAsArray.controls; let i=index" class = "chip" >
< div class = "uk-label uk-flex uk-flex-middle" >
< span class = "uk-text-truncate uk-width-expand" > { { getLabel ( chip . value ) } } < / span >
< icon ( click ) = " remove ( i ) " class = "uk-link-text uk-margin-small-left clickable" [ flex ] = " true "
name = "close" ratio = "0.7" > < / icon >
< / div >
< / div >
< div class = "uk-width-expand uk-flex uk-flex-column uk-flex-center" >
< input # searchInput class = "input" [ class.search ] = " searchControl.value "
[ attr . placeholder ] = "placeholderInfo.static?placeholderInfo.label:hint" [ formControl ] = "searchControl" >
< / div >
2022-03-30 19:47:47 +02:00
< / div >
2022-03-31 15:39:17 +02:00
< / n g - t e m p l a t e >
< div * ngIf = "formControl.disabled || icon || type === 'select' || type === 'autocomplete'"
class = "uk-margin-small-left uk-margin-right icon" >
< icon * ngIf = "formControl.disabled" [ name ] = " ' lock ' " [ flex ] = " true " > < / icon >
< ng - template [ ngIf ] = " formControl.enabled " >
< icon * ngIf = "!formControl.value && icon" [ name ] = " icon " [ flex ] = " true " > < / icon >
< icon * ngIf = "!icon && type === 'select'" name = "arrow_drop_down" [ flex ] = " true " > < / icon >
< icon * ngIf = "formControl.value && type === 'autocomplete'" class = "clickable" ( click ) = " resetSearch ( $ event ) " [ flex ] = " true " name = "close" > < / icon >
< / n g - t e m p l a t e >
2022-03-30 19:47:47 +02:00
< / div >
2022-03-28 00:21:55 +02:00
< / div >
2022-03-31 15:39:17 +02:00
< div class = "tools" >
< ng - content select = "[tools]" > < / n g - c o n t e n t >
< / div >
2022-03-28 00:21:55 +02:00
< / div >
< / div >
2022-03-31 15:39:17 +02:00
< div class = "options uk-dropdown" * ngIf = "filteredOptions && filteredOptions.length > 1 && opened" # optionBox
uk - dropdown = "pos: bottom-justify; mode: hover; offset: 15;" >
2022-03-30 19:47:47 +02:00
< ul class = "uk-nav uk-dropdown-nav" >
2022-03-31 15:39:17 +02:00
< li * ngFor = "let option of filteredOptions" [ class.uk - active ] = " formControl.value = = = option.value " [ attr.uk - tooltip ] = " ( tooltip ) ? ( ' title : ' + option.label + ' ; ' ) : null " >
2022-03-30 19:47:47 +02:00
< a ( click ) = " selectOption ( option , $ event ) " > { { option . label } } < / a >
< / li >
< / ul >
< / div >
< span * ngIf = "formControl.invalid && formControl.touched" class = "uk-text-danger" >
2022-03-28 00:21:55 +02:00
< span * ngIf = "formControl.errors.error" > { { formControl . errors . error } } < / span >
2022-03-31 15:39:17 +02:00
< span * ngIf = "type === 'URL' || type === 'logoURL'" > Please provide a valid URL ( e . g . https : //example.com)</span>
2022-03-28 00:21:55 +02:00
< / span >
< span class = "uk-text-danger uk-text-small" >
< ng - content select = "[error]" > < / n g - c o n t e n t >
< / span >
< span * ngIf = "formControl.valid" class = "uk-text-warning uk-text-small" >
< ng - content select = "[warning]" > < / n g - c o n t e n t >
< span * ngIf = "!secure" >
< span class = "uk-text-bold" > Note : < / span > Prefer urls like "<span class=" uk - text - bold ">https://</span>example.com/my-secure-image.png"
instead of "<span class=" uk - text - bold ">http://</span>example.com/my-image.png" .
< span class = "uk-text-bold" > Browsers may not load non secure content . < / span >
< / span >
< / span >
< span class = "uk-text-small" >
< ng - content select = "[note]" > < / n g - c o n t e n t >
< / span >
2022-03-31 15:39:17 +02:00
`
2019-12-23 14:59:56 +01:00
} )
2022-03-28 00:21:55 +02:00
export class InputComponent implements OnInit , OnDestroy , AfterViewInit , OnChanges {
2022-03-31 15:39:17 +02:00
/** Deprecated options*/
2022-03-28 00:21:55 +02:00
/** @deprecated */
2019-12-23 14:59:56 +01:00
@Input ( 'label' ) label : string ;
2022-03-28 00:21:55 +02:00
/** @deprecated */
2020-10-19 11:06:23 +02:00
@Input ( ) extraLeft : boolean = true ;
2022-03-28 00:21:55 +02:00
/** @deprecated */
2021-01-21 16:15:53 +01:00
@Input ( ) gridSmall : boolean = false ;
2022-03-28 00:21:55 +02:00
/** @deprecated */
2020-10-19 17:05:16 +02:00
@Input ( ) hideControl : boolean = false ;
2022-03-28 00:21:55 +02:00
/** @deprecated */
2021-02-12 12:31:12 +01:00
@Input ( ) flex : 'middle' | 'top' | 'bottom' = 'middle' ;
2022-03-28 00:21:55 +02:00
/** @deprecated */
2020-11-01 16:41:02 +01:00
@Input ( ) iconLeft : boolean = false ;
2022-03-31 15:39:17 +02:00
/** @deprecated */
2021-01-28 17:33:16 +01:00
@Input ( ) removable : boolean = true ;
2022-03-31 15:39:17 +02:00
/** @deprecated */
2021-02-17 19:44:02 +01:00
@Input ( ) smallChip : boolean = false ;
2022-03-31 15:39:17 +02:00
/** @deprecated */
2021-02-17 19:44:02 +01:00
@Input ( ) panelWidth : number = 300 ;
2022-03-31 15:39:17 +02:00
/** @deprecated */
2021-02-17 19:44:02 +01:00
@Input ( ) panelClass : string = null ;
2022-03-31 15:39:17 +02:00
/** Basic information */
@Input ( 'formInput' ) formControl : AbstractControl ;
@Input ( 'type' ) type : 'text' | 'URL' | 'logoURL' | 'autocomplete' | 'textarea' | 'select' | 'chips' = 'text' ;
@Input ( ) value : any | any [ ] ;
@Output ( ) valueChange = new EventEmitter < any | any [ ] > ( ) ;
@Input ( 'hint' ) hint : string ;
/** Text */
@ViewChild ( 'input' ) input : ElementRef ;
/** Textarea options */
@ViewChild ( 'textArea' ) textArea : ElementRef ;
@Input ( 'rows' ) rows : number = 3 ;
/** Select | Autocomplete | chips available options */
@Input ( 'options' ) options : Option [ ] = [ ] ;
@Input ( ) tooltip : boolean = false ;
/** Chips && Autocomplete*/
public filteredOptions : Option [ ] = [ ] ;
public searchControl : FormControl ;
/** Use modifier's class(es) to change view of your Input */
@Input ( ) inputClass : string = 'inner' ;
/** Icon on the input */
@Input ( ) icon : string = null ;
/** Chip options */
@Input ( ) addExtraChips : boolean = false ;
2021-02-24 17:53:31 +01:00
@Input ( ) showOptionsOnEmpty : boolean = true ;
2022-01-11 12:15:37 +01:00
@Input ( ) validators : ValidatorFn [ ] | ValidatorFn ;
2022-03-28 00:21:55 +02:00
@Input ( ) disabled : boolean = false ;
2021-02-12 12:31:12 +01:00
@Output ( ) focusEmitter : EventEmitter < boolean > = new EventEmitter < boolean > ( ) ;
2021-03-03 19:04:53 +01:00
/** LogoUrl information */
public secure : boolean = true ;
2021-02-12 12:31:12 +01:00
/** Internal basic information */
2022-03-31 15:39:17 +02:00
public placeholderInfo : Placeholder = { label : '' , static : true } ;
2020-06-05 16:48:12 +02:00
public required : boolean = false ;
2022-03-28 00:21:55 +02:00
public focused : boolean = false ;
public opened : boolean = false ;
public properties : EnvProperties = properties ;
2019-12-23 14:59:56 +01:00
private initValue : any ;
2022-03-28 00:21:55 +02:00
private subscriptions : any [ ] = [ ] ;
@ViewChild ( 'inputBox' ) inputBox : ElementRef ;
@ViewChild ( 'optionBox' ) optionBox : ElementRef ;
2021-02-12 12:31:12 +01:00
@ViewChild ( 'searchInput' ) searchInput : ElementRef ;
2020-10-19 11:06:23 +02:00
2022-03-31 15:39:17 +02:00
@Input ( )
set placeholder ( placeholder : string | Placeholder ) {
if ( typeof placeholder === 'string' ) {
this . placeholderInfo = { label : placeholder , static : false } ;
} else {
if ( placeholder . static && ( this . type === 'select' || this . type === 'autocomplete' || this . type === 'chips' || this . hint ) ) {
placeholder . static = false ;
console . debug ( 'Static placeholder is not available in this type of input and if hint is available.' ) ;
}
this . placeholderInfo = placeholder ;
}
}
2021-02-12 12:31:12 +01:00
constructor ( private elementRef : ElementRef ) {
2022-03-28 00:21:55 +02:00
if ( elementRef . nativeElement . hasAttribute ( 'dashboard-input' ) && this . properties . environment === "development" ) {
console . warn ( "'dashboard-input' selector is deprecated; use 'input' instead." ) ;
}
2021-02-12 12:31:12 +01:00
}
@HostListener ( 'document:click' , [ '$event' ] )
2022-03-28 00:21:55 +02:00
click ( event ) {
2022-03-30 19:47:47 +02:00
this . focus ( this . inputBox && this . inputBox . nativeElement . contains ( event . target ) ) ;
2019-12-23 14:59:56 +01:00
}
2020-10-19 11:06:23 +02:00
2022-03-28 00:21:55 +02:00
ngOnInit() {
2022-03-30 19:47:47 +02:00
if ( ! this . formControl ) {
if ( Array . isArray ( this . value ) ) {
2022-03-28 00:21:55 +02:00
this . formControl = new FormArray ( [ ] ) ;
this . value . forEach ( value = > {
this . formAsArray . push ( new FormControl ( value , this . validators ) ) ;
} ) ;
} else {
this . formControl = new FormControl ( this . value ) ;
this . formControl . setValidators ( this . validators ) ;
}
2022-03-30 19:47:47 +02:00
if ( this . disabled ) {
2022-03-28 00:21:55 +02:00
this . formControl . disable ( ) ;
}
}
}
2022-02-08 10:52:11 +01:00
ngAfterViewInit() {
2020-10-19 11:06:23 +02:00
this . reset ( ) ;
}
ngOnChanges ( changes : SimpleChanges ) {
2022-03-30 19:47:47 +02:00
if ( changes . formControl || changes . validators || changes . options ) {
2020-10-19 11:06:23 +02:00
this . reset ( ) ;
}
}
2022-03-30 19:47:47 +02:00
ngOnDestroy ( ) : void {
this . unsubscribe ( ) ;
}
2022-03-28 00:21:55 +02:00
get formAsControl ( ) : FormControl {
if ( this . formControl instanceof FormControl ) {
return this . formControl ;
} else {
return null ;
}
}
2021-01-28 17:33:16 +01:00
get formAsArray ( ) : FormArray {
2022-03-28 00:21:55 +02:00
if ( this . formControl instanceof FormArray ) {
return this . formControl ;
} else {
return null ;
}
2021-01-28 17:33:16 +01:00
}
2020-10-19 11:06:23 +02:00
reset() {
2021-03-03 19:04:53 +01:00
this . secure = true ;
2020-10-19 11:06:23 +02:00
this . unsubscribe ( ) ;
2019-12-23 14:59:56 +01:00
this . initValue = HelperFunctions . copy ( this . formControl . value ) ;
2022-03-28 00:21:55 +02:00
if ( this . type === 'logoURL' ) {
2021-03-03 19:04:53 +01:00
this . secure = ( ! this . initValue || this . initValue . includes ( 'https://' ) ) ;
}
2022-03-30 19:47:47 +02:00
if ( this . options ) {
this . filteredOptions = this . filter ( '' ) ;
}
2021-03-22 16:10:18 +01:00
if ( this . type === 'chips' || this . type === 'autocomplete' ) {
2022-03-30 19:47:47 +02:00
this . searchControl = new FormControl ( '' , this . validators ) ;
this . subscriptions . push ( this . searchControl . valueChanges . subscribe ( value = > {
this . filteredOptions = this . filter ( value ) ;
if ( this . focused ) {
this . open ( true ) ;
2021-03-22 16:10:18 +01:00
setTimeout ( ( ) = > {
this . searchInput . nativeElement . focus ( ) ;
this . searchInput . nativeElement . value = value ;
2022-03-28 00:21:55 +02:00
} , 0 ) ;
2022-03-30 19:47:47 +02:00
}
} ) ) ;
2021-01-28 17:33:16 +01:00
}
2020-10-19 11:06:23 +02:00
if ( this . formControl && this . formControl . validator ) {
2020-06-05 16:48:12 +02:00
let validator = this . formControl . validator ( { } as AbstractControl ) ;
this . required = ( validator && validator . required ) ;
}
2020-10-19 11:06:23 +02:00
this . subscriptions . push ( this . formControl . valueChanges . subscribe ( value = > {
2021-02-12 12:31:12 +01:00
value = ( value === '' ) ? null : value ;
2022-03-28 00:21:55 +02:00
if ( this . type === 'logoURL' ) {
2021-03-03 19:04:53 +01:00
this . secure = ( ! value || value . includes ( 'https://' ) ) ;
}
2021-02-12 12:31:12 +01:00
if ( this . initValue === value || ( this . initValue === '' && value === null ) ) {
2019-12-23 14:59:56 +01:00
this . formControl . markAsPristine ( ) ;
2022-03-28 11:14:16 +02:00
} else {
this . formControl . markAsDirty ( ) ;
2019-12-23 14:59:56 +01:00
}
2022-03-28 00:21:55 +02:00
if ( this . searchControl ) {
2021-03-22 16:10:18 +01:00
this . searchControl . setValue ( null ) ;
}
2022-03-28 00:21:55 +02:00
this . value = this . formControl . value ;
this . valueChange . emit ( this . value ) ;
2020-10-19 11:06:23 +02:00
} ) ) ;
2022-03-28 00:21:55 +02:00
if ( this . input ) {
2022-02-08 10:52:11 +01:00
this . input . nativeElement . disabled = this . formControl . disabled ;
}
2020-10-19 11:06:23 +02:00
}
unsubscribe() {
this . subscriptions . forEach ( subscription = > {
2021-02-12 12:31:12 +01:00
if ( subscription instanceof Subscription ) {
2020-10-19 11:06:23 +02:00
subscription . unsubscribe ( ) ;
}
2019-12-23 14:59:56 +01:00
} ) ;
}
2020-10-19 11:06:23 +02:00
2019-12-23 14:59:56 +01:00
stopPropagation() {
2020-10-19 11:06:23 +02:00
event . stopPropagation ( ) ;
2019-12-23 14:59:56 +01:00
}
2021-01-28 17:33:16 +01:00
2022-03-30 19:47:47 +02:00
remove ( index : number ) {
2021-01-28 17:33:16 +01:00
this . formAsArray . removeAt ( index ) ;
this . formAsArray . markAsDirty ( ) ;
2022-03-30 19:47:47 +02:00
this . focus ( true ) ;
2021-01-28 17:33:16 +01:00
this . searchControl . setValue ( '' ) ;
2021-02-12 12:31:12 +01:00
this . stopPropagation ( ) ;
2021-01-28 17:33:16 +01:00
}
private filter ( value : string ) : Option [ ] {
2021-03-19 15:23:47 +01:00
let options = this . options ;
2022-03-28 00:21:55 +02:00
if ( this . type === "chips" ) {
options = options . filter ( option = > ! this . formAsArray . value . find ( value = > HelperFunctions . equals ( option . value , value ) ) ) ;
2021-03-19 15:23:47 +01:00
}
if ( ( ! value || value . length == 0 ) ) {
2022-03-28 00:21:55 +02:00
return ( this . showOptionsOnEmpty ) ? options : [ ] ;
2021-01-28 17:33:16 +01:00
}
const filterValue = value . toString ( ) . toLowerCase ( ) ;
return options . filter ( option = > option . label . toLowerCase ( ) . indexOf ( filterValue ) != - 1 ) ;
}
2021-02-12 12:31:12 +01:00
2022-03-30 19:47:47 +02:00
add() {
if ( this . addExtraChips && this . searchControl . value && this . searchControl . valid ) {
2021-02-12 12:31:12 +01:00
this . stopPropagation ( ) ;
2022-03-30 19:47:47 +02:00
this . formAsArray . push ( new FormControl ( this . searchControl . value , this . validators ) ) ;
2021-02-12 12:31:12 +01:00
this . formAsArray . markAsDirty ( ) ;
2022-03-30 19:47:47 +02:00
this . focus ( true ) ;
2021-02-12 12:31:12 +01:00
this . searchControl . setValue ( '' ) ;
}
}
getLabel ( value : any ) {
2021-04-21 17:28:35 +02:00
let option = this . options . find ( option = > HelperFunctions . equals ( option . value , value ) ) ;
2021-02-12 12:31:12 +01:00
return ( option ) ? option.label : value ;
}
2021-03-19 15:23:47 +01:00
2022-03-30 19:47:47 +02:00
focus ( value : boolean ) {
if ( this . focused ) {
this . formControl . markAsTouched ( ) ;
}
this . focused = value ;
if ( this . focused ) {
if ( this . input ) {
this . input . nativeElement . focus ( ) ;
} else if ( this . textArea ) {
this . textArea . nativeElement . focus ( ) ;
} else if ( this . searchInput ) {
this . searchInput . nativeElement . focus ( ) ;
}
if ( this . type === 'select' ) {
this . open ( ! this . opened ) ;
} else if ( this . type !== 'autocomplete' || ! this . formControl . value ) {
this . open ( true ) ;
}
} else {
this . open ( false ) ;
if ( this . searchControl ) {
this . add ( ) ;
}
}
this . focusEmitter . emit ( this . focused ) ;
}
2022-03-28 00:21:55 +02:00
open ( value : boolean ) {
this . opened = value && this . formControl . enabled ;
setTimeout ( ( ) = > {
2022-03-30 19:47:47 +02:00
if ( this . optionBox ) {
if ( this . opened ) {
UIkit . dropdown ( this . optionBox . nativeElement ) . show ( ) ;
} else {
UIkit . dropdown ( this . optionBox . nativeElement ) . hide ( ) ;
}
}
} , 0 ) ;
2022-03-28 00:21:55 +02:00
}
2021-03-22 16:10:18 +01:00
resetSearch ( event : any ) {
2021-03-19 15:23:47 +01:00
event . stopPropagation ( ) ;
2022-03-28 00:21:55 +02:00
this . formControl . markAsDirty ( ) ;
2021-03-19 15:23:47 +01:00
this . formControl . setValue ( null ) ;
2022-03-30 19:47:47 +02:00
this . focus ( true ) ;
this . searchControl . setValue ( '' ) ;
2021-03-19 15:23:47 +01:00
}
2022-03-28 00:21:55 +02:00
2022-03-30 19:47:47 +02:00
selectOption ( option : Option , event ) {
if ( this . formControl . enabled ) {
if ( this . formAsControl ) {
this . formAsControl . setValue ( option . value ) ;
} else if ( this . formAsArray ) {
this . formAsArray . push ( new FormControl ( option . value ) ) ;
this . formAsArray . markAsDirty ( ) ;
event . stopPropagation ( ) ;
this . focus ( true ) ;
this . searchControl . setValue ( '' ) ;
}
2022-03-28 00:21:55 +02:00
}
}
2019-12-23 14:59:56 +01:00
}