import { Component, forwardRef, Input, ViewEncapsulation, ChangeDetectorRef, ChangeDetectionStrategy, HostListener, OnInit, OnChanges, SimpleChanges, Provider, inject, } from "@angular/core"; import { NG_VALUE_ACCESSOR } from "@angular/forms"; import { SamFormControl } from "../../form-controls/sam-form-control"; import { SamFormService } from "../../form-service"; export const VALUE_ACCESSOR: Provider = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SamInputMaskComponent), multi: true, }; @Component({ selector: "sam-input-mask", template: ``, providers: [VALUE_ACCESSOR], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, }) export class SamInputMaskComponent extends SamFormControl implements OnInit, OnChanges { cdr: ChangeDetectorRef; service: SamFormService; @Input() template: string; @Input() placeholder: string; @Input() disableFocusBehavior: boolean = false; @Input() maxlength: number; previousVal: string; pattern = /([^_\/\)\(-\s])/g; defaultValue = ""; protected _value: string = null; public get value(): string { return this._value; } public set value(val: string | null | undefined) { this._value = !val ? this.defaultValue : val; } constructor() { const cdr = inject(ChangeDetectorRef); const service = inject(SamFormService); super(); this.cdr = cdr; this.service = service; } @HostListener("focus") onHostFocus() { this.onTouched(); } onFocus() { if (!this.disableFocusBehavior) { this.value = this.templateToNumber(this.value); } } onBlur() { const newVal = this.numberToTemplate(this.value); this.value = newVal; if (newVal !== this.previousVal) { this.previousVal = this.value; this.onChange(this.value); this.cdr.detectChanges(); } } onModelChange(newVal: string) { this.value = newVal; if (this.previousVal && this._value === "") { this.onChange(this._value); this.cdr.detectChanges(); } else if (this.maxlength && newVal && newVal.length === this.maxlength) { this.onChange(this._value); this.cdr.detectChanges(); } } ngOnChanges(changes: SimpleChanges) { if (changes["maxlength"] && typeof this.maxlength !== "number") { throw Error( 'Wrong data type passed in for maxlength. Expected "number", got ' + typeof this.maxlength ); } } ngOnInit() { if (!this.template) { throw Error("No template provided"); } } templateToNumber(template: string): string { if (!template) { return; } else { return template .split("") .filter((char) => char.match(this.pattern)) .join(""); } } numberToTemplate(numberStr: string): string { if (!numberStr) { return; } numberStr = numberStr.replace(/\D/g, ""); const digits = numberStr .split("") .filter((digit) => digit.match(this.pattern)); const blanks = this.template ? this.template.split("") : []; return blanks .map((blank) => { if (blank === "_") { const next = digits.shift(); return next ? next : blank; } else { return blank; } }) .join("") .concat(digits.join("")); } writeValue(val: string | null | undefined) { this.value = val; this.previousVal = val; this.cdr.detectChanges(); } }