2021-02-02 14:47:58 +05:30
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
2022-01-04 13:54:25 +05:30
// Copyright (C) 2013 - 2022, The pgAdmin Development Team
2021-02-02 14:47:58 +05:30
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import React , { useRef , useState , useEffect } from 'react' ;
import { useDelayDebounce } from 'sources/custom_hooks' ;
import { onlineHelpSearch } from './online_help' ;
import { menuSearch } from './menuitems_help' ;
import $ from 'jquery' ;
import gettext from 'sources/gettext' ;
export function Search ( ) {
const wrapperRef = useRef ( null ) ;
const [ searchTerm , setSearchTerm ] = useState ( '' ) ;
const [ isShowMinLengthMsg , setIsShowMinLengthMsg ] = useState ( false ) ;
const [ isMenuLoading , setIsMenuLoading ] = useState ( false ) ;
const [ isHelpLoading , setIsHelpLoading ] = useState ( false ) ;
const [ menuSearchResult , setMenuSearchResult ] = useState ( {
fetched : false ,
data : [ ] ,
} ) ;
const [ helpSearchResult , setHelpSearchResult ] = useState ( {
fetched : false ,
clearedPooling : true ,
url : '' ,
data : [ ] ,
} ) ;
const [ showResults , setShowResults ] = useState ( false ) ;
const resetSearchState = ( ) => {
setMenuSearchResult ( state => ( {
... state ,
fetched : false ,
data : [ ] ,
} ) ) ;
setHelpSearchResult ( state => ( {
... state ,
fetched : false ,
clearedPooling : true ,
url : '' ,
data : { } ,
} ) ) ;
} ;
// Below will be called when any changes has been made to state
useEffect ( ( ) => {
2022-09-08 20:28:43 +05:30
if ( menuSearchResult . fetched ) {
2021-02-02 14:47:58 +05:30
setIsMenuLoading ( false ) ;
}
2022-09-08 20:28:43 +05:30
if ( helpSearchResult . fetched ) {
2021-02-02 14:47:58 +05:30
setIsHelpLoading ( false ) ;
}
} , [ menuSearchResult , helpSearchResult ] ) ;
const initSearch = ( param ) => {
setIsMenuLoading ( true ) ;
setIsHelpLoading ( true ) ;
onlineHelpSearch ( param , {
state : helpSearchResult ,
setState : setHelpSearchResult ,
} ) ;
menuSearch ( param , {
state : menuSearchResult ,
setState : setMenuSearchResult ,
} ) ;
} ;
// Debounse logic to avoid multiple re-render with each keypress
useDelayDebounce ( initSearch , searchTerm , 1000 ) ;
const toggleDropdownMenu = ( ) => {
let pooling = window . pooling ;
if ( pooling ) {
window . clearInterval ( pooling ) ;
}
document . getElementsByClassName ( 'live-search-field' ) [ 0 ] . value = '' ;
setTimeout ( function ( ) {
document . getElementById ( 'live-search-field' ) . focus ( ) ;
} , 100 ) ;
resetSearchState ( ) ;
setShowResults ( ! showResults ) ;
setIsMenuLoading ( false ) ;
setIsHelpLoading ( false ) ;
setIsShowMinLengthMsg ( false ) ;
} ;
const refactorMenuItems = ( items ) => {
if ( items . length > 0 ) {
let menuItemsHtmlElement = [ ] ;
for ( let i = 0 ; i < items . length ; i ++ ) {
2021-05-29 12:55:59 +05:30
Object . keys ( items [ i ] ) . forEach ( ( value ) => {
2021-02-02 14:47:58 +05:30
if ( value != 'element' && value != 'No object selected' ) {
2022-09-08 20:28:43 +05:30
menuItemsHtmlElement . push ( < li key = { 'li-menu-' + i } > < a tabIndex = '0' id = { 'li-menu-' + i } href = { '#' } className = { ( items [ i ] [ 'element' ] . classList . contains ( 'disabled' ) ? 'dropdown-item menu-groups-a disabled' : 'dropdown-item menu-groups-a' ) } key = { 'menu-' + i } onClick = { ( ) => { items [ i ] [ 'element' ] . click ( ) ; toggleDropdownMenu ( ) ; } } >
2021-02-02 14:47:58 +05:30
{ value }
< span key = { 'menu-span-' + i } > { refactorPathToMenu ( items [ i ] [ value ] ) } < / s p a n >
< / a >
2022-09-08 20:28:43 +05:30
{ ( ( items [ i ] [ 'element' ] . classList . contains ( 'disabled' ) && items [ i ] [ 'element' ] . getAttribute ( 'data-disabled' ) != undefined ) ? < i className = 'fa fa-info-circle quick-search-tooltip' data - toggle = 'tooltip' title = { items [ i ] [ 'element' ] . getAttribute ( 'data-disabled' ) } aria - label = 'Test data tooltip' aria - hidden = 'true' > < / i > : ' ' ) }
2021-02-02 14:47:58 +05:30
< / l i > ) ;
}
} ) ;
}
$ ( '[data-toggle="tooltip"]' ) . tooltip ( ) ;
return menuItemsHtmlElement ;
}
} ;
const refactorPathToMenu = ( path ) => {
if ( path ) {
let pathArray = path . split ( '/' ) ;
let spanElement = [ ] ;
for ( let i = 0 ; i < pathArray . length ; i ++ ) {
if ( i == ( pathArray . length - 1 ) ) {
spanElement . push ( pathArray [ i ] ) ;
} else {
spanElement . push ( < span key = { 'menu-span-sub' + i } > { pathArray [ i ] } < i className = 'fa fa-angle-right' aria - hidden = 'true' > < / i > < / s p a n > ) ;
}
}
return spanElement ;
}
} ;
const onInputValueChange = ( value ) => {
let pooling = window . pooling ;
if ( pooling ) {
window . clearInterval ( pooling ) ;
}
resetSearchState ( ) ;
setSearchTerm ( '' ) ;
if ( value . length >= 3 ) {
setSearchTerm ( value ) ;
setIsMenuLoading ( true ) ;
setIsHelpLoading ( true ) ;
setIsShowMinLengthMsg ( false ) ;
} else {
setIsMenuLoading ( false ) ;
setIsHelpLoading ( false ) ;
}
if ( value . length < 3 && value . length > 0 ) {
setIsShowMinLengthMsg ( true ) ;
}
if ( value . length == 0 ) {
setIsShowMinLengthMsg ( false ) ;
}
} ;
const useOutsideAlerter = ( ref ) => {
useEffect ( ( ) => {
/ * *
* Alert if clicked on outside of element
* /
function handleClickOutside ( event ) {
if ( ref . current && ! ref . current . contains ( event . target ) ) {
let input _element = document . getElementById ( 'live-search-field' ) ;
2021-02-03 12:15:37 +05:30
if ( input _element == null ) {
return ;
}
2021-02-02 14:47:58 +05:30
let input _value = input _element . value ;
if ( input _value && input _value . length > 0 ) {
toggleDropdownMenu ( ) ;
}
}
}
// Bind the event listener
document . addEventListener ( 'mousedown' , handleClickOutside ) ;
return ( ) => {
// Unbind the event listener on clean up
document . removeEventListener ( 'mousedown' , handleClickOutside ) ;
} ;
} , [ ref ] ) ;
} ;
useOutsideAlerter ( wrapperRef ) ;
return (
< div id = 'quick-search-container' onClick = { setSearchTerm } > < / d i v > ,
< ul id = 'quick-search-container' ref = { wrapperRef } className = 'test' role = "menu" >
< li >
< ul id = 'myDropdown' >
< li className = 'dropdown-item-input' >
< input tabIndex = '0' autoFocus type = 'text' autoComplete = 'off' className = 'form-control live-search-field'
aria - label = 'live-search-field' id = 'live-search-field' placeholder = { gettext ( 'Quick Search' ) } onChange = { ( e ) => { onInputValueChange ( e . target . value ) ; } } / >
< / l i >
< div style = { { marginBottom : 0 } } >
< div >
{ isShowMinLengthMsg
? ( < div className = 'pad-12 no-results' >
< span className = 'fa fa-info-circle' > < / s p a n >
& nbsp ; Please enter minimum 3 characters to search
< / d i v > )
: '' }
< div >
2022-09-08 20:28:43 +05:30
{ ( menuSearchResult . fetched && ! isMenuLoading ) ?
2021-02-02 14:47:58 +05:30
< div >
< div className = 'menu-groups' >
< span className = 'fa fa-window-maximize' > < / s p a n > & n b s p ; { g e t t e x t ( ' M E N U I T E M S ' ) } ( { m e n u S e a r c h R e s u l t . d a t a . l e n g t h } )
< / d i v >
{ refactorMenuItems ( menuSearchResult . data ) }
< / d i v > : ( ( i s M e n u L o a d i n g ) ? ( < d i v c l a s s N a m e = ' p a d - 1 2 ' > < d i v c l a s s N a m e = " s e a r c h - i c o n " > { g e t t e x t ( ' S e a r c h i n g . . . ' ) } < / d i v > < / d i v > ) : ' ' ) }
2022-09-08 20:28:43 +05:30
{ ( menuSearchResult . data . length == 0 && menuSearchResult . fetched && ! isMenuLoading ) ? ( < div className = 'pad-12 no-results' > < span className = 'fa fa-info-circle' > < / s p a n > { g e t t e x t ( ' N o s e a r c h r e s u l t s ' ) } < / d i v > ) : ' ' }
2021-02-02 14:47:58 +05:30
2022-09-08 20:28:43 +05:30
{ ( helpSearchResult . fetched && ! isHelpLoading ) ?
2021-02-02 14:47:58 +05:30
< div >
< div className = 'help-groups' >
< span className = 'fa fa-question-circle' > < / s p a n > & n b s p ; { g e t t e x t ( ' H E L P A R T I C L E S ' ) } { O b j e c t . k e y s ( h e l p S e a r c h R e s u l t . d a t a ) . l e n g t h > 1 0 ?
< span > ( 10 of { Object . keys ( helpSearchResult . data ) . length } )
< / s p a n > :
'(' + Object . keys ( helpSearchResult . data ) . length + ')' } & nbsp ;
{ ! helpSearchResult . clearedPooling ? < img src = '/static/img/loading.gif' alt = { gettext ( 'Loading...' ) } className = 'help_loading_icon' / > : '' }
{ Object . keys ( helpSearchResult . data ) . length > 10 ? < a href = { helpSearchResult . url } className = 'pull-right no-padding' target = '_blank' rel = 'noreferrer' > { gettext ( 'Show all' ) } & nbsp ; < span className = 'fas fa-external-link-alt' > < / s p a n > < / a > : ' ' }
< / d i v >
{ Object . keys ( helpSearchResult . data ) . map ( ( value , index ) => {
if ( index <= 9 ) { return < li key = { 'li-help-' + index } > < a tabIndex = '0' href = { helpSearchResult . data [ value ] } key = { 'help-' + index } className = 'dropdown-item' target = '_blank' rel = 'noreferrer' > { value } < / a > < / l i > ; }
} ) }
{ ( Object . keys ( helpSearchResult . data ) . length == 0 ) ? ( < div className = 'pad-12 no-results' > < span className = 'fa fa-info-circle' > < / s p a n > { g e t t e x t ( ' N o s e a r c h r e s u l t s ' ) } < / d i v > ) : ' ' }
2022-09-08 20:28:43 +05:30
< / d i v > : ( ( i s H e l p L o a d i n g & & ! i s M e n u L o a d i n g ) ? (
2021-02-02 14:47:58 +05:30
< div >
< div className = 'help-groups' >
< span className = 'fa fa-question-circle' > < / s p a n >
& nbsp ; HELP ARTICLES
{ Object . keys ( helpSearchResult . data ) . length > 10
? '(10 of ' + Object . keys ( helpSearchResult . data ) . length + ')'
: '(' + Object . keys ( helpSearchResult . data ) . length + ')'
}
{ Object . keys ( helpSearchResult . data ) . length > 10
? < a href = { helpSearchResult . url } className = 'pull-right no-padding' target = '_blank' rel = 'noreferrer' >
Show all & nbsp ; < span className = 'fas fa-external-link-alt' > < / s p a n > < / a > : ' '
}
< / d i v >
< div className = 'pad-12' > < div className = "search-icon" > { gettext ( 'Searching...' ) } < / d i v > < / d i v >
< / d i v > ) : ' ' ) }
< / d i v >
< / d i v >
< / d i v >
< / u l >
< / l i >
< div id = 'quick-search-iframe-container' / >
< / u l >
) ;
}