/********************************************************************************
 * Copyright (C) 2020 Ericsson and others.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the Eclipse
 * Public License v. 2.0 are satisfied: GNU General Public License, version 2
 * with the GNU Classpath Exception which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 ********************************************************************************/ import { codicon , ReactWidget , StatefulWidget } from '@theia/core/lib/browser' ; import { injectable , postConstruct } from '@theia/core/shared/inversify' ; import * as React from '@theia/core/shared/react' ; import debounce = require ( 'p-debounce' ) ; import { Disposable , Emitter } from '@theia/core' ; import { nls } from '@theia/core/lib/common/nls' ; export interface PreferencesSearchbarState { searchTerm : string ; } @ injectable ( ) export class PreferencesSearchbarWidget extends ReactWidget implements StatefulWidget { static readonly ID = 'settings.header' ; static readonly LABEL = 'Settings Header' ; static readonly SEARCHBAR_ID = 'preference-searchbar' ; protected readonly onFilterStringChangedEmitter = new Emitter < string > ( ) ; readonly onFilterChanged = this . onFilterStringChangedEmitter . event ; protected searchbarRef : React . RefObject < HTMLInputElement > = React . createRef < HTMLInputElement > ( ) ; protected resultsCount : number = 0 ; @ postConstruct ( ) protected init ( ) { this . onRender . push ( Disposable . create ( ( ) => this . focus ( ) ) ) ; this . id = PreferencesSearchbarWidget . ID ; this . title . label = PreferencesSearchbarWidget . LABEL ; this . update ( ) ; } protected handleSearch = ( e ) : Promise < void > => this . search ( e . target . value ) ; protected search = debounce ( async ( value ) => { this . onFilterStringChangedEmitter . fire ( value ) ; this . update ( ) ; } , 200 ) ; focus ( ) { if ( this . searchbarRef . current ) { this . searchbarRef . current . focus ( ) ; } } param e protected clearSearchResults = async ( e ) : Promise < void > => { const search = document . getElementById ( PreferencesSearchbarWidget . SEARCHBAR_ID ) as HTMLInputElement ; if ( search ) { search . value = '' ; await this . search ( search . value ) ; this . update ( ) ; } } ; /**
     * Renders all search bar options.
     */ protected renderOptionContainer ( ) { const resultsCount = this . renderResultsCountOption ( ) ; const clearAllOption = this . renderClearAllOption ( ) ; return < div className = "option-buttons" > { resultsCount } { clearAllOption } < /div>; } /**
     * Renders a badge displaying search results count.
     */ protected renderResultsCountOption ( ) { let resultsFound ; if ( this . resultsCount === 0 ) { resultsFound = nls . localizeByDefault ( 'No Settings Found' ) ; } else if ( this . resultsCount === 1 ) { resultsFound = nls . localizeByDefault ( '1 Setting Found' ) ; } else { resultsFound = nls . localizeByDefault ( '{0} Settings Found' , this . resultsCount . toFixed ( 0 ) ) ; } return this . searchTermExists ( ) ? ( < span className = "results-found" title = { resultsFound } > { resultsFound } < /span>) : '' ; } /**
     * Renders a clear all button.
     */ protected renderClearAllOption ( ) { return < span className = { `${ codicon ( 'clear-all' ) } option ${ ( this . searchTermExists ( ) ? 'enabled' : '' ) }` } title = { nls . localizeByDefault ( 'Clear Search Results' ) } onClick = { this . clearSearchResults } / > ; } returns protected searchTermExists ( ) { return ! ! this . searchbarRef . current ?. value ; } protected getSearchTerm ( ) { const search = document . getElementById ( PreferencesSearchbarWidget . SEARCHBAR_ID ) as HTMLInputElement ; return search ?. value ; } async updateSearchTerm ( searchTerm ) { const search = document . getElementById ( PreferencesSearchbarWidget . SEARCHBAR_ID ) as HTMLInputElement ; if ( ! search || search . value === searchTerm ) { return ; } search . value = searchTerm ; await this . search ( search . value ) ; this . update ( ) ; } render ( ) { const optionContainer = this . renderOptionContainer ( ) ; return ( < div className = 'settings-header' > < div className = "settings-search-container" > < input type = "text" id = { PreferencesSearchbarWidget . SEARCHBAR_ID } spellCheck = { false } placeholder = { nls . localizeByDefault ( 'Search Settings' ) } className = "settings-search-input theia-input" onChange = { this . handleSearch } ref = { this . searchbarRef } / > { optionContainer } < /div> < /div>); } param count updateResultsCount ( count ) { this . resultsCount = count ; this . update ( ) ; } storeState ( ) { return { searchTerm : this . getSearchTerm ( ) } ; } restoreState ( oldState ) { const searchInputExists = this . onDidChangeVisibility ( ( ) => { this . updateSearchTerm ( oldState . searchTerm || '' ) ; searchInputExists . dispose ( ) ; } ) ; } }