import { Component , EventEmitter , Input , Output , } from '@angular/core' ; type SearchResult = any ; export interface SearchHandler { ( text ) : Array < SearchResult > | Promise < Array < SearchResult > > ; } export enum SearchState { Idle , Searching , Results , Failure } @ Component ( { selector : 'search' , templateUrl : './search.html' , styleUrls : [ './search.css' ] , } ) export class Search { @ Input ( ) private handler : SearchHandler ; @ Input ( ) placeholder : string ; @ Output ( ) private selectedResult = new EventEmitter < SearchResult > ( ) ; SearchState = SearchState ; state = SearchState . Idle ; query : string ; private results : Array < SearchResult > ; current : number ; get total ( ) : number { switch ( this . state ) { case SearchState . Idle : case SearchState . Failure : case SearchState . Searching : return 0 ; default : return this . results == null || this . results . length === 0 ? 0 : this . results . length ; } } enableNext ( ) { return this . current < this . total ; } enablePrevious ( ) { return this . current > 0 ; } onKeypress ( event ) { if ( this . state !== SearchState . Results ) { return ; } if ( event . key === 'Enter' ) { if ( event . getModifierState ( 'Shift' ) ) { this . previous ( ) ; } else { this . next ( ) ; } } } onSearchChanged ( value ) { this . query = value ; const response = ( state , results ) => { this . state = state ; this . results = results ; this . current = 0 ; if ( results . length > 0 ) { this . selectedResult . emit ( this . results [ this . current ] ) ; } } ; if ( this . query == null || this . query . length === 0 ) { response ( SearchState . Idle , new Array < SearchResult > ( ) ) ; return ; } this . state = SearchState . Searching ; const result = this . handler ( this . query ) ; if ( Array . isArray ( result ) ) { response ( SearchState . Results , result ) ; } else if ( typeof result . then === 'function' ) { result . then ( searchResults => { response ( SearchState . Results , searchResults ) ; } ) . catch ( error => { response ( SearchState . Failure , new Array < SearchResult > ( ) ) ; } ) ; } } previous ( ) { if ( -- this . current < 0 ) { this . current = this . total - 1 ; } this . selectedResult . emit ( this . results [ this . current ] ) ; } next ( ) { if ( ++ this . current >= this . total ) { this . current = 0 ; } this . selectedResult . emit ( this . results [ this . current ] ) ; } reload ( ) { this . onSearchChanged ( this . query ) ; } reset ( ) { this . onSearchChanged ( '' ) ; } }