swimlane/ngx-ui

View on GitHub
projects/swimlane/ngx-ui/src/lib/components/input/input-autosize.directive.ts

Summary

Maintainability
B
4 hrs
Test Coverage
import { ElementRef, Directive, Input, AfterContentInit } from '@angular/core';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { NgModel } from '@angular/forms';
import { delay, filter, take } from 'rxjs/operators';

@Directive({
  exportAs: 'ngxAutosize',
  selector: 'textarea[autosize], input[autosize]',
  host: {
    class: 'ngx-autosize',
    '(input)': 'onInput()'
  }
})
export class AutosizeDirective implements AfterContentInit {
  @Input('autosize')
  get enabled() {
    return this._enabled;
  }
  set enabled(v: boolean) {
    this._enabled = coerceBooleanProperty(v);
  }
  private _enabled = false;

  get nodeName() {
    return this.element.nativeElement.nodeName as 'TEXTAREA' | 'INPUT';
  }

  constructor(
    readonly element: ElementRef<HTMLInputElement | HTMLTextAreaElement>,
    private readonly ngModel: NgModel
  ) {}

  ngAfterContentInit(): void {
    if (this.ngModel) {
      this.ngModel.valueChanges
        .pipe(
          filter(value => !!value && value?.length > 0),
          take(1),
          delay(0) // delay is added as the scrollHeight of textarea is 0 even though there is value
        )
        .subscribe(() => {
          this.onInput();
        });
    }
  }

  onInput() {
    if (this._enabled) {
      const nativeEl = this.element.nativeElement;

      if (this.nodeName === 'TEXTAREA') {
        nativeEl.style.height = 'auto';

        if (nativeEl.clientHeight < nativeEl.scrollHeight) {
          nativeEl.style.height = `${nativeEl.scrollHeight}px`;
        }
      } else {
        nativeEl.style.width = 'auto';

        if (nativeEl.clientWidth < nativeEl.scrollWidth) {
          nativeEl.style.width = `${nativeEl.scrollWidth}px`;
        }
      }
    }
  }
}