Project

General

Profile

1
import {Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild} from "@angular/core";
2
import {AbstractControl, FormArray, FormControl} from "@angular/forms";
3
import {HelperFunctions} from "../../utils/HelperFunctions.class";
4
import {Observable, of, Subscription} from "rxjs";
5
import {MatSelect} from "@angular/material/select";
6
import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
7
import {map, startWith} from "rxjs/operators";
8

    
9

    
10
export interface Option {
11
  icon?: string,
12
  iconClass?: string,
13
  value: any,
14
  label: string
15
}
16

    
17
@Component({
18
  selector: '[dashboard-input]',
19
  template: `
20
    <div *ngIf="label && type != 'checkbox'"
21
         class="uk-text-bold uk-form-label uk-margin-small-bottom">{{label + (required ? ' *' : '')}}</div>
22
    <div *ngIf="hint" class="uk-margin-bottom uk-text-small uk-form-hint">{{hint}}</div>
23
    <div class="uk-grid uk-flex uk-flex-middle" [class.uk-grid-small]="gridSmall" uk-grid>
24
      <ng-content></ng-content>
25
      <div [class.uk-hidden]="hideControl" class="uk-width-expand uk-position-relative"
26
           [class.uk-flex-first]="!extraLeft">
27
        <ng-template [ngIf]="icon && formControl.enabled">
28
          <span class="uk-text-muted" [ngClass]="iconLeft?('left'):'right'">
29
            <icon [name]="icon"></icon>
30
          </span>
31
        </ng-template>
32
        <ng-template [ngIf]="formControl.disabled">
33
          <span class="uk-text-muted left">
34
            <icon [name]="'lock'"></icon>
35
          </span>
36
        </ng-template>
37
        <ng-template [ngIf]="type === 'text'">
38
          <input class="uk-input input-box uk-text-small"
39
                 [attr.uk-tooltip]="formControl.disabled?'title: This field is not editable; pos: bottom-left':''"
40
                 [placeholder]="placeholder" [formControl]="formControl"
41
                 [class.uk-form-danger]="formControl.invalid && formControl.touched">
42
        </ng-template>
43
        <ng-template [ngIf]="type === 'textarea'">
44
          <textarea class="uk-textarea input-box uk-text-small"
45
                    [attr.uk-tooltip]="formControl.disabled?'title: This field is not editable; pos: bottom-left':''"
46
                    [rows]="rows" [placeholder]="placeholder"
47
                    [formControl]="formControl" [class.uk-form-danger]="formControl.invalid && formControl.touched">
48
          </textarea>
49
        </ng-template>
50
        <ng-template [ngIf]="type === 'select'">
51
          <div class="input-box"
52
               [attr.uk-tooltip]="formControl.disabled?'title: This field is not editable; pos: bottom-left':null"
53
               [class.clickable]="formControl.enabled"
54
               [class.uk-form-danger]="formControl.invalid && formControl.touched" (click)="openSelect()">
55
            <mat-form-field class="uk-width-1-1">
56
              <mat-select #select [required]="required" [value]="null"
57
                          (openedChange)="stopPropagation()" [formControl]="formControl"
58
                          [disableOptionCentering]="true">
59
                <mat-option *ngIf="placeholder" class="uk-hidden" [value]="''">{{placeholder}}</mat-option>
60
                <mat-option *ngFor="let option of options" [value]="option.value">
61
                  {{option.label}}
62
                </mat-option>
63
              </mat-select>
64
            </mat-form-field>
65
          </div>
66
        </ng-template>
67
        <ng-template [ngIf]="type === 'chips'">
68
          <div class="input-box"
69
               [attr.uk-tooltip]="formControl.disabled?'title: This field is not editable; pos: bottom-left':null"
70
               [class.clickable]="formControl.enabled"
71
               [class.uk-form-danger]="formControl.invalid && formControl.touched" (click)="openSelect()">
72
            <mat-form-field class="uk-width-1-1">
73
              <mat-chip-list #chipList aria-label="Page selection">
74
                <mat-chip *ngFor="let chip of formAsArray.controls; let i=index"
75
                          [removable]="removable">
76
                  {{chip.value[chipLabel]}}
77
                  <icon name="remove_circle" class="mat-chip-remove" (click)="removed(i)"></icon>
78
                </mat-chip>
79
                <div class="uk-width-expand uk-position-relative uk-text-small chip-input">
80
                  <input #searchInput class="uk-width-1-1" [formControl]="searchControl" [matAutocomplete]="auto" [matChipInputFor]="chipList">
81
                  <div *ngIf="placeholder && !searchControl.value" class="placeholder uk-width-1-1"
82
                       (click)="searchInput.focus()">{{placeholder}}</div>
83
                </div>
84
              </mat-chip-list>
85
              <mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)">
86
                <mat-option *ngFor="let option of filteredOptions | async" [value]="option.value">
87
                  {{option.label}}
88
                </mat-option>
89
              </mat-autocomplete>
90
            </mat-form-field>
91
          </div>
92
        </ng-template>
93
        <span *ngIf="formControl.invalid && formControl.errors.error"
94
              class="uk-text-danger input-message">{{formControl.errors.error}}</span>
95
        <span *ngIf="warning" class="uk-text-warning input-message">{{warning}}</span>
96
        <span *ngIf="note" class="input-message">{{note}}</span>
97
      </div>
98
    </div>
99
    <mat-checkbox *ngIf="type === 'checkbox'" [formControl]="formControl">{{label}}</mat-checkbox>
100
  `,
101
  styleUrls: ['input.component.css']
102
})
103
export class InputComponent implements OnInit, OnDestroy, OnChanges {
104
  @Input('formInput') formControl: AbstractControl;
105
  @Input('type') type: 'text' | 'textarea' | 'select' | 'checkbox' | 'chips' = 'text';
106
  @Input('label') label: string;
107
  @Input('rows') rows: number = 3;
108
  @Input('options') options: Option[];
109
  @Input('hint') hint = null;
110
  @Input('placeholder') placeholder = '';
111
  @ViewChild('select') select: MatSelect;
112
  @Input() extraLeft: boolean = true;
113
  @Input() gridSmall: boolean = false;
114
  @Input() hideControl: boolean = false;
115
  @Input() icon: string = null;
116
  @Input() iconLeft: boolean = false;
117
  @Input() warning: string = null;
118
  @Input() note: string = null;
119
  @Input() removable: boolean = true;
120
  @Input() chipLabel: string = null;
121
  public filteredOptions: Observable<Option[]>;
122
  public searchControl: FormControl;
123
  public required: boolean = false;
124
  private initValue: any;
125
  private subscriptions: any[] = [];
126
  @ViewChild('searchInput') searchInput;
127
  
128
  constructor() {
129
  }
130
  
131
  ngOnInit(): void {
132
    this.reset();
133
  }
134
  
135
  ngOnChanges(changes: SimpleChanges) {
136
    if(changes.formControl) {
137
      this.reset();
138
    }
139
  }
140
  
141
  get formAsArray(): FormArray {
142
    return (<FormArray> this.formControl);
143
  }
144
  
145
  reset() {
146
    this.unsubscribe();
147
    this.initValue = HelperFunctions.copy(this.formControl.value);
148
    if(this.options && this.type === 'chips') {
149
      this.filteredOptions = of(this.options);
150
      this.searchControl = new FormControl('');
151
      this.filteredOptions = this.searchControl.valueChanges.pipe(startWith(''),
152
        map(option => this.filter(option)));
153
    }
154
    if (this.formControl && this.formControl.validator) {
155
      let validator = this.formControl.validator({} as AbstractControl);
156
      this.required = (validator && validator.required);
157
    }
158
    this.subscriptions.push(this.formControl.valueChanges.subscribe(value => {
159
      value = (value === '')?null:value;
160
      if(this.initValue === value || (this.initValue === '' && value === null)) {
161
        this.formControl.markAsPristine();
162
      }
163
    }));
164
    if (!this.formControl.value) {
165
      this.formControl.setValue('');
166
    }
167
  }
168
  
169
  unsubscribe() {
170
    this.subscriptions.forEach(subscription => {
171
      if(subscription instanceof Subscription) {
172
        subscription.unsubscribe();
173
      }
174
    });
175
  }
176
  
177
  openSelect() {
178
    if(this.select) {
179
      this.select.open();
180
    }
181
  }
182
  
183
  ngOnDestroy(): void {
184
    this.unsubscribe();
185
  }
186
  
187
  stopPropagation() {
188
    event.stopPropagation();
189
  }
190
  
191
  removed(index: number) {
192
    this.formAsArray.removeAt(index);
193
    this.formAsArray.markAsDirty();
194
  }
195
  
196
  selected(event: MatAutocompleteSelectedEvent): void {
197
    this.formAsArray.push(new FormControl(event.option.value));
198
    this.formAsArray.markAsDirty();
199
    this.searchControl.setValue('');
200
    this.searchInput.nativeElement.value = '';
201
  }
202
  
203
  private filter(value: string): Option[] {
204
    let options = this.options.filter(option => !this.formAsArray.value.find(value => option.label === value[this.chipLabel]));
205
    if (!value || value.length == 0) {
206
      return options;
207
    }
208
    const filterValue = value.toString().toLowerCase();
209
    return options.filter(option => option.label.toLowerCase().indexOf(filterValue) != -1);
210
  }
211
}
(2-2/3)