/// /// Copyright 2015-2026 Micro Focus or one of its affiliates. /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /* eslint-disable @angular-eslint/no-output-native */ import { ChangeDetectorRef, Directive, ElementRef, EventEmitter, forwardRef, HostListener, inject, Output, } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; export const TOOLBAR_SEARCH_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ToolbarSearchFieldDirective), multi: true, }; @Directive({ selector: '[uxToolbarSearchField]', providers: [TOOLBAR_SEARCH_VALUE_ACCESSOR], }) export class ToolbarSearchFieldDirective implements ControlValueAccessor { private readonly _elementRef = inject(ElementRef); private readonly _changeDetector = inject(ChangeDetectorRef); /** Emit whenever the escape key is pressed */ @Output() cancel = new EventEmitter(); /** Emit whenever the enter key is pressed */ @Output() submitted = new EventEmitter(); /** Get the current value of the input control */ get text(): string { return this._elementRef.nativeElement.value; } /** For use with the Forms and ReactiveForms */ // eslint-disable-next-line @typescript-eslint/no-empty-function private onTouchedCallback: () => void = () => {}; /** Call this function with the latest value to update ngModel or formControl name */ // eslint-disable-next-line @typescript-eslint/no-empty-function private onChangeCallback: (_: any) => void = () => {}; focus(): void { // mark the control as dirty this.onTouchedCallback(); // focus the input control after a delay to ensure the element is present requestAnimationFrame(() => this._elementRef.nativeElement.focus()); } blur(): void { // blur the input control after a delay to ensure the element is present requestAnimationFrame(() => this._elementRef.nativeElement.blur()); } /** Clear the input, if we have an ngModel reset its value otherwise just set the input value to empty */ clear(): void { this.setValue(''); } @HostListener('keydown.enter') onEnter(): void { this.submitted.emit(this.text); } @HostListener('keydown.escape') onEscape(): void { this._elementRef.nativeElement.blur(); this.cancel.emit(); } @HostListener('input') onInput(): void { this.setValue(this.text); } /** Update the input value based on ngModel or formControl */ writeValue(value: string): void { this.setValue(value); this._changeDetector.markForCheck(); } /** Register a function to update form control */ registerOnChange(fn: any): void { this.onChangeCallback = fn; } /** Register a function to mark form control as touched */ registerOnTouched(fn: any): void { this.onTouchedCallback = fn; } /** Update the value in all required places */ private setValue(value: string): void { // ngModel/form control can set the default value to null or undefined, which can show in the input. Replace with empty string if (!value) { value = ''; } // update the form value if there is one in use this.onChangeCallback(value); // update the content of the input control this._elementRef.nativeElement.value = value; } }