all files / component/autosize-textarea/ autosize-textarea.directive.ts

97.3% Statements 36/37
50% Branches 1/2
92.31% Functions 12/13
97.06% Lines 33/34
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82                                                                                                 
/*
 *  @license
 *  Copyright Hôpitaux Universitaires de Genève. All Rights Reserved.
 *
 *  Use of this source code is governed by an Apache-2.0 license that can be
 *  found in the LICENSE file at https://github.com/DSI-HUG/dejajs-components/blob/master/LICENSE
 */
 
import { coerceNumberProperty } from '@angular/cdk/coercion';
import { AfterViewInit, Directive, ElementRef, forwardRef, Input, OnDestroy } from '@angular/core';
import { NG_VALIDATORS, Validator } from '@angular/forms';
import {from as observableFrom, of as observableOf ,  Subject } from 'rxjs';
import {debounceTime, delay, first, takeWhile, tap} from 'rxjs/operators';
 
/**
 * Directive pour rendre un textarea material redimensioné automatiquement au contenu.
 * Implémentation (créer un champ mat-form-field-container>textarea et lui ajouter la directive deja-autosize
 * Attention, comme la directive utilise un validateur pour détecter les modifications de contenu du textarea, le textarea doit impérativement utiliser ngModel.
 */
@Directive({
    providers: [
        { provide: NG_VALIDATORS, useExisting: forwardRef(() => DejaAutosizeTextAreaDirective), multi: true },
    ],
    selector: 'textarea[deja-autosize]',
})
export class DejaAutosizeTextAreaDirective implements AfterViewInit, Validator, OnDestroy {
 
    /** Définit le nombre de lignes lorsque aucun text ne figure dans le contrôle */
    @Input()
    public set rows(value: number | string) {
        this._rows = coerceNumberProperty(value);
 
        observableOf(this._rows).pipe(
            first(),
            tap((rows) => {
                this.textAreaElement.setAttribute('rows', (rows || 1).toString());
                this.textAreaElement.style.overflowY = 'hidden';
            }),
            delay(1), )
            .subscribe(() => {
                this.minHeight = this.textAreaElement.scrollHeight;
                this.textAreaElement.setAttribute('rows', '1');
            });
    }
 
    public get rows() {
        return this._rows;
    }
 
    private _rows = 1;
    private resize$ = new Subject<void>();
    private isAlive = true;
    private textAreaElement: HTMLTextAreaElement;
    private minHeight = 0;
 
    constructor(private elementRef: ElementRef) {
        this.textAreaElement = this.elementRef.nativeElement;
 
        observableFrom(this.resize$).pipe(
            takeWhile(() => this.isAlive),
            debounceTime(5),
            tap(() => this.textAreaElement.style.height = `${this.minHeight}px`), )
            .subscribe(() => {
                this.textAreaElement.style.height = `${this.textAreaElement.scrollHeight}px`;
            });
    }
 
    public ngOnDestroy() {
        this.isAlive = false;
    }
 
    public ngAfterViewInit() {
        this.resize$.next();
    }
 
    public validate(): { [key: string]: any } {
        this.resize$.next();
        return null;
    }
}