import { Directive, ElementRef, HostListener, Injector, Input, } from '@angular/core'; import { InputPatternPipe } from '../pipes/input-pattern.pipe'; import { ICaInput } from '../config'; import { InputTransformStringEnum } from '../enums/input-text-transform.enum'; import { RestrictionPipeMixin } from '../mixins/restriction-pipe.mixin'; @Directive({ selector: '[restrictInput]', standalone: true, providers: [InputPatternPipe], // Provide the pipe so it can be injected }) export class RestrictInputDirective extends RestrictionPipeMixin( class { restrictInput!: ICaInput; } ) { @Input() override restrictInput!: ICaInput; private inputPatternPipe!: InputPatternPipe; private lastValidInputValue: string = ''; constructor( private el: ElementRef, private injector: Injector ) { super(); this.inputPatternPipe = this.injector.get(InputPatternPipe); // Inject pipe manually } @HostListener('input', ['$event']) onInput(event: InputEvent): void { let inputValue = this.el.nativeElement.value; const maxLength = this.restrictInput.maxLength; const configName = this.restrictInput.name.toLowerCase(); const { regex, restrictConsecutiveDots, restrictMultipeDots, restrictConsecutiveSpaces, isDecimalAndDotOnly, } = this.inputPatternPipe.transform(configName, this.restrictInput); // Prevent first space character if (inputValue.startsWith(' ')) { inputValue = inputValue.trimStart(); // Remove leading space } // Allow dots but dont allow on first place... const dotIndex = inputValue.indexOf('.'); if (dotIndex == 0) { if (this.restrictInput.priceSeparator) { inputValue = `0${inputValue}`; } else inputValue = ''; } const inputValueToTestWithRegex = isDecimalAndDotOnly ? inputValue.replace(/[^0-9.]/g, '') : inputValue; if (regex.test(inputValueToTestWithRegex)) { if (maxLength) { // Allow only numbers and one dot (.) const tempLengthValue = isDecimalAndDotOnly ? inputValue.replace(/[^0-9.]/g, '') : inputValue; const [integerPart, decimalPart] = tempLengthValue.split('.'); // Ensure integer part respects maxLength (excluding thousand separators) if (integerPart.length > maxLength!) { inputValue = integerPart.substring(0, maxLength) + (decimalPart ? '.' + decimalPart : ''); } } if (restrictConsecutiveSpaces) { inputValue = inputValue.replace(/\s{2,}/g, ' '); // Replace consecutive spaces with a single space } if (restrictConsecutiveDots) { inputValue = inputValue.replace(/\.{2,}/g, '.'); // Replace consecutive dots with a single dot } if (restrictMultipeDots) { inputValue = inputValue.replace( /^([^.]*)\.(.*)$/, (m: any, p1: any, p2: any) => p1 + '.' + p2.replace(/\./g, '') ); } this.el.nativeElement.value = inputValue; // 🔥 Manually trigger max Directive because current one needs to be done first this.el.nativeElement.dispatchEvent( new Event('applyMaxValueDirective') ); const transformText = this.getTextTransformation( inputValue, this.restrictInput?.textTransform as InputTransformStringEnum ); if (transformText) this.el.nativeElement.value = transformText; if (!this.checkRangeRegex(this.el.nativeElement.value)) { // If entered is not passing regEx then the input value will be last saved... this.el.nativeElement.value = this.lastValidInputValue; } else { // Save last valid value this.lastValidInputValue = inputValue; } } else { // If entered is not passing regEx then the input value will be last saved... this.el.nativeElement.value = this.lastValidInputValue; } } }