Source

lib/components/searchbar/searchbar.component.ts

import { Component,  EventEmitter, HostListener, Input, OnInit, Output } from '@angular/core';
import { AutocompleteTypes, PredefinedColors} from '@ionic/core';
import { StringOrBoolean } from '../../engine/types';
import {windowEventEmitter} from '../../helpers/utils';
import { ForAngularModule } from '../../for-angular.module';
import { stringToBoolean } from '../../helpers/utils';
import { NgxBaseComponent } from '../../engine/NgxBaseComponent';
import { IonSearchbar } from '@ionic/angular/standalone';
import * as allIcons from 'ionicons/icons';
import { addIcons } from 'ionicons';

/**
 * @description Searchbar component for Angular applications.
 * @summary The SearchbarComponent provides a highly customizable search input field with comprehensive
 * options for appearance, behavior, and interaction patterns. It extends NgxBaseComponent to inherit
 * common functionality and implements OnInit for proper lifecycle management. This component features
 * debounced input handling, window event integration, visibility controls, and extensive styling options.
 * It's designed to be flexible and adaptable to different search requirements within modern web applications.
 *
 * @class SearchbarComponent
 * @extends {NgxBaseComponent}
 * @implements {OnInit}
 * @memberOf SearchbarComponent
 */
@Component({
  selector: 'ngx-decaf-searchbar',
  templateUrl: './searchbar.component.html',
  styleUrls: ['./searchbar.component.scss'],
  standalone: true,
  imports: [ForAngularModule, IonSearchbar],
})
export class SearchbarComponent extends NgxBaseComponent implements OnInit {

  /**
   * @description The mode of the searchbar.
   * @summary Determines the visual style of the searchbar, either iOS or Material Design.
   * @type {"ios" | "md" | undefined}
   * @default "ios"
   */
  // @Input()
  // override mode: "ios" | "md" | undefined = "md";

  /**
   * @description The autocomplete attribute for the searchbar input.
   * @summary Specifies whether the browser should enable autocomplete for the input field.
   * This controls the browser's built-in autocomplete functionality, helping users by
   * suggesting previously entered values or common inputs. Setting to 'off' disables
   * this feature for privacy or security reasons.
   *
   * @type {AutocompleteTypes | undefined}
   * @default "off"
   * @memberOf SearchbarComponent
   */
  @Input()
  autocomplete: AutocompleteTypes | undefined = "off";

  /**
   * @description The autocorrect attribute for the searchbar input.
   * @summary Controls whether the browser should enable autocorrect functionality for the input field.
   * When enabled, the browser will automatically correct spelling mistakes as the user types.
   * This is typically disabled for search fields to preserve the user's exact search terms.
   *
   * @type {"on" | "off"}
   * @default "off"
   * @memberOf SearchbarComponent
   */
  @Input()
  autocorrect: "on" | "off" = "off";

  /**
   * @description Whether the searchbar should animate.
   * @summary Controls the animation behavior of the searchbar during appearance and disappearance transitions.
   * When enabled, the searchbar will use smooth animations for state changes, providing a more
   * polished user experience. This affects transitions like showing/hiding the component.
   *
   * @type {StringOrBoolean}
   * @default true
   * @memberOf SearchbarComponent
   */
  @Input()
  animated: StringOrBoolean = true;

  /**
   * @description The text for the cancel button.
   * @summary Specifies the localized text to be displayed on the cancel button of the searchbar.
   * This text appears when the cancel button is visible and provides users with a clear
   * indication of how to dismiss the search interface. The text can be customized for
   * different languages and cultural contexts.
   *
   * @type {string}
   * @default "Cancel"
   * @memberOf SearchbarComponent
   */
  @Input()
  buttonCancelText: string = "Cancel";

  /**
   * @description The icon to use for the clear button.
   * @summary Specifies the icon to be displayed for the clear button of the searchbar.
   * @type {string | undefined}
   * @default undefined
   * @memberOf SearchbarComponent
   */
  @Input()
  clearIcon: string | undefined = undefined;

  /**
   * @description The color of the searchbar.
   * @summary Specifies the color theme to be applied to the searchbar.
   * @type {string | undefined}
   * @default undefined
   * @memberOf SearchbarComponent
   */
  @Input()
  color: string | undefined = undefined;

  /**
   * @description The amount of time, in milliseconds, to wait to trigger the `ionChange` event after each keystroke.
   * @summary Controls the debounce time for the search input to reduce the frequency of event emissions.
   * @type {number}
   * @default 500
   * @memberOf SearchbarComponent
   */
  @Input()
  debounce: number = 500;

  /**
   * @description Whether the searchbar is disabled.
   * @summary Controls whether the searchbar is interactive or not.
   * @type {StringOrBoolean}
   * @default false
   * @memberOf SearchbarComponent
   */
  @Input()
  disabled: StringOrBoolean = false;

  /**
   * @description A hint to the browser for which enter key to display.
   * @summary Specifies the type of action that will be performed when the enter key is pressed.
   * @type {"search" | "enter" | "done" | "go" | "next" | "previous" | "send" | undefined}
   * @default "enter"
   * @memberOf SearchbarComponent
   */
  @Input()
  enterkeyhint: "search" | "enter" | "done" | "go" | "next" | "previous" | "send" | undefined = "enter";

  /**
   * @description The input mode for the searchbar.
   * @summary Specifies the type of data that might be entered by the user while editing the element or its contents.
   * @type {"text" | "search" | "none" | "email" | "tel" | "url" | "numeric" | "decimal" | undefined}
   * @default 'search'
   * @memberOf SearchbarComponent
   */
  @Input()
  inputmode: "text" | "search" | "none" | "email" | "tel" | "url" | "numeric" | "decimal" | undefined = 'search';

  /**
   * @description The placeholder for the searchbar input.
   * @summary Specifies the placeholder text to be displayed in the searchbar when it's empty.
   * @type {string}
   * @default "Search"
   * @memberOf SearchbarComponent
   */
  @Input()
  placeholder = "Search";

  /**
   * @description The icon to use for the search button.
   * @summary Specifies the icon to be displayed for the search button of the searchbar.
   * @type {string | undefined}
   * @default "search-outline"
   * @memberOf SearchbarComponent
   */
  @Input()
  searchIcon: string | undefined = "search-outline";

  /**
   * @description When to show the cancel button.
   * @summary Controls the visibility of the cancel button in different states of the searchbar.
   * @type {"always" | "focus" | "never"}
   * @default "never"
   * @memberOf SearchbarComponent
   */
  @Input()
  showCancelButton: "always" | "focus" | "never" = "never";

  /**
   * @description When to show the clear button.
   * @summary Controls the visibility of the clear button in different states of the searchbar.
   * @type {"always" | "focus" | "never"}
   * @default "focus"
   * @memberOf SearchbarComponent
   */
  @Input()
  showClearButton: "always" | "focus" | "never" = "focus";

  /**
   * @description Whether to enable spellcheck on the searchbar input.
   * @summary Controls whether the browser's spellcheck feature is enabled for the searchbar input.
   * @type {boolean}
   * @default false
   * @memberOf SearchbarComponent
   */
  @Input()
  spellcheck: boolean = false;

  /**
   * @description The type of input to use for the searchbar.
   * @summary Specifies the type of control to display for the searchbar input.
   * @type {"number" | "text" | "search" | "email" | "password" | "tel" | "url" | undefined}
   * @default "search"
   * @memberOf SearchbarComponent
   */
  @Input()
  type: "number" | "text" | "search" | "email" | "password" | "tel" | "url" | undefined = "search";

  /**
   * @description The value of the searchbar input.
   * @summary Specifies the current value of the searchbar input.
   * @type {null | string | undefined}
   * @default ""
   * @memberOf SearchbarComponent
   */
  @Input()
  value: null | string | undefined = "";

  /**
   * @description The keys to use for querying.
   * @summary Specifies the keys to be used when performing a search query.
   * @type {string | string[]}
   * @default "name"
   * @memberOf SearchbarComponent
   */
  @Input()
  queryKeys: string | string[] = "name";

  /**
   * @description Whether the searchbar is visible.
   * @summary Controls the visibility of the searchbar component.
   * @type {StringOrBoolean}
   * @default false
   * @memberOf SearchbarComponent
   */
  @Input()
  isVisible: StringOrBoolean = false;

  /**
   * @description Whether to wrap the searchbar in a container.
   * @summary Controls whether the searchbar is wrapped in an additional container element.
   * @type {StringOrBoolean}
   * @default false
   * @memberOf SearchbarComponent
   */
  @Input()
  wrapper: StringOrBoolean = false;

  /**
   * @description The color of the wrapper.
   * @summary Specifies the color theme to be applied to the wrapper container, if present.
   * @type {PredefinedColors}
   * @default "primary"
   * @memberOf SearchbarComponent
   */
  @Input()
  wrapperColor: PredefinedColors = "primary";

  /**
   * @description Whether to emit events to the window.
   * @summary Controls whether search events should be emitted as window events.
   * @type {StringOrBoolean}
   * @default true
   * @memberOf SearchbarComponent
   */
  @Input()
  emitEventToWindow: StringOrBoolean = true;

  /**
   * @description The current value of the searchbar.
   * @summary Stores the current value of the searchbar input for internal state management and processing.
   * This property is used to track the search term throughout the component's lifecycle and
   * coordinate between different event handlers and methods.
   *
   * @type {string | undefined}
   * @memberOf SearchbarComponent
   */
  currentValue: string | undefined;

  /**
   * @description Event emitter for search events.
   * @summary Emits search events when the user interacts with the searchbar, providing a reactive
   * interface for parent components to respond to search actions. This event is triggered by
   * various user interactions including typing, clearing, and explicit search actions.
   *
   * @type {EventEmitter<string>}
   * @memberOf SearchbarComponent
   */
  @Output()
  searchEvent: EventEmitter<string> = new EventEmitter<string>();

  /**
   * @description Creates an instance of SearchbarComponent.
   * @summary Initializes the SearchbarComponent with all necessary dependencies and configurations.
   * During initialization, it adds all available Ionicons to the application's icon registry,
   * ensuring that search and clear icons are available for use throughout the component's lifecycle.
   *
   * @memberOf SearchbarComponent
   */
  constructor() {
    super('SearchbarComponent');
    addIcons(allIcons)
  }

  /**
   * @description Initializes the component after Angular first displays the data-bound properties.
   * @summary Performs essential component initialization by converting string-based boolean inputs
   * to proper boolean values using the stringToBoolean utility. This ensures that all boolean
   * properties work correctly regardless of how they were passed from parent components or templates.
   *
   * @return {void}
   *
   * @mermaid
   * sequenceDiagram
   *   participant A as Angular Lifecycle
   *   participant S as SearchbarComponent
   *   participant U as Utility Functions
   *
   *   A->>S: ngOnInit()
   *   S->>U: stringToBoolean(emitEventToWindow)
   *   U-->>S: boolean value
   *   S->>U: stringToBoolean(wrapper)
   *   U-->>S: boolean value
   *   S->>U: stringToBoolean(isVisible)
   *   U-->>S: boolean value
   *   S->>U: stringToBoolean(disabled)
   *   U-->>S: boolean value
   *   S->>U: stringToBoolean(animated)
   *   U-->>S: boolean value
   *   Note over S: Component ready for interaction
   *
   * @memberOf SearchbarComponent
   */
  ngOnInit(): void {
    this.emitEventToWindow = stringToBoolean(this.emitEventToWindow);
    this.wrapper = stringToBoolean(this.wrapper);
    this.isVisible = stringToBoolean(this.isVisible);
    this.disabled = stringToBoolean(this.disabled);
    this.animated = stringToBoolean(this.animated);
  }

  /**
   * @description Handles the visibility toggle of the searchbar component.
   * @summary Listens for global window events to toggle the visibility state of the searchbar.
   * When the searchbar becomes visible, it automatically focuses on the input field after a brief
   * delay to ensure smooth animation completion. This provides a seamless user experience for
   * search activation through keyboard shortcuts or programmatic triggers.
   *
   * @param {CustomEvent} event - The custom event triggering the visibility toggle (unused but required by HostListener)
   * @return {void}
   *
   * @mermaid
   * sequenceDiagram
   *   participant W as Window
   *   participant S as SearchbarComponent
   *   participant E as DOM Element
   *
   *   W->>S: toggleSearchbarVisibility event
   *   S->>S: handleToggleVisibility()
   *   S->>S: Toggle isVisible state
   *   alt isVisible is true AND component exists
   *     S->>S: setTimeout(125ms)
   *     S->>E: setFocus() on ion-searchbar
   *   end
   *
   * @memberOf SearchbarComponent
   */
  @HostListener("window:toggleSearchbarVisibility", ['$event'])
  handleToggleVisibility(): void {
    this.isVisible = !this.isVisible;
    if(this.isVisible && !!this.component.nativeElement) {
      setTimeout(() => {
        (this.component.nativeElement as HTMLIonSearchbarElement).setFocus();
      }, 125);
    }
  }

  /**
   * @description Triggers a manual search event with the current searchbar value.
   * @summary Retrieves the current value from the searchbar's native element and emits it as a search event.
   * This method provides a programmatic way to trigger search functionality, useful for external
   * components or keyboard shortcuts that need to execute search without user interaction with the searchbar itself.
   *
   * @return {void}
   * @memberOf SearchbarComponent
   */
  search(): void {
    const element = this.component.nativeElement as HTMLIonSearchbarElement;
    this.searchEvent.emit(element.value || undefined);
  }

  /**
   * @description Handles value changes in the searchbar input field.
   * @summary Processes change events from the Ionic searchbar component and extracts the new value
   * to emit as a search event. This method is triggered when the user finishes editing the searchbar
   * value, providing a way to react to completed input changes rather than real-time typing.
   *
   * @param {CustomEvent} event - The change event from the Ionic searchbar containing the new value
   * @return {void}
   * @memberOf SearchbarComponent
   */
  handleChange(event: CustomEvent): void {
    this.emitEvent(event?.detail?.value ?? undefined);
  }

  /**
   * @description Handles clearing of the searchbar input field.
   * @summary Emits an undefined value as a search event when the searchbar is cleared by the user.
   * This method is typically triggered when the user clicks the clear button or uses other
   * clear mechanisms, signaling that the search should be reset or cleared.
   *
   * @return {void}
   * @memberOf SearchbarComponent
   */
  handleClear(): void {
    this.emitEvent(undefined);
  }

  /**
   * @description Handles real-time input events on the searchbar.
   * @summary Processes input events as the user types, providing immediate feedback for search functionality.
   * This method implements smart clearing behavior - if the input becomes empty, it automatically
   * triggers the clear handler. Otherwise, it emits the current value for real-time search suggestions
   * or filtering. This enables responsive search experiences with debounced event handling.
   *
   * @param {CustomEvent} event - The input event from the Ionic searchbar containing the current value
   * @return {void}
   *
   * @mermaid
   * sequenceDiagram
   *   participant U as User
   *   participant S as SearchbarComponent
   *   participant E as Event System
   *
   *   U->>S: Type in searchbar
   *   S->>S: handleInput(event)
   *   S->>S: Extract value from event
   *   alt value is empty or null
   *     S->>S: handleClear()
   *     S->>E: Emit undefined
   *   else value has content
   *     S->>S: emitEvent(value)
   *     S->>E: Emit search value
   *   end
   *
   * @memberOf SearchbarComponent
   */
  handleInput(event: CustomEvent): void {
    const value = event?.detail?.value;
    if(!value || !value?.length)
      return this.handleClear();
    this.emitEvent(value);
  }

  /**
   * @description Handles blur events on the searchbar.
   * @summary Currently an empty method, can be implemented for specific blur behavior.
   * @param {CustomEvent} event - The blur event from the searchbar
   * @return {void}
   */
  // handleBlur(event: CustomEvent): void {}

  /**
   * @description Emits search events through multiple channels.
   * @summary Orchestrates the emission of search events both as component output events and optionally
   * as global window events. This dual-channel approach enables both direct parent-child communication
   * and application-wide event broadcasting, supporting flexible integration patterns and loose coupling
   * between components that need to respond to search actions.
   *
   * @param {string | undefined} value - The search value to emit across all configured channels
   * @return {void}
   *
   * @mermaid
   * sequenceDiagram
   *   participant S as SearchbarComponent
   *   participant P as Parent Component
   *   participant W as Window Event System
   *
   *   S->>S: emitEvent(value)
   *   S->>P: searchEvent.emit(value)
   *   alt emitEventToWindow is true
   *     S->>W: windowEventEmitter('searchbarEvent', {value})
   *   end
   *
   * @memberOf SearchbarComponent
   */
  emitEvent(value: string | undefined): void {
    this.searchEvent.emit(value);
    if(this.emitEventToWindow)
      windowEventEmitter('searchbarEvent', {value: value})
  }

  /**
   * @description Prevents default behavior of DOM events.
   * @summary Utility method to prevent unwanted default actions on DOM events, such as form submissions
   * or navigation triggers. This is commonly used in event handlers where the default browser behavior
   * would interfere with the component's custom logic or user experience design.
   *
   * @param {Event} event - The DOM event whose default behavior should be prevented
   * @return {void}
   * @memberOf SearchbarComponent
   */
  preventChange(event: Event): void {
     event.preventDefault();
  }
}