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';
|
2022-12-06 18:16:36 +05:30
|
|
|
import { makeStyles } from '@material-ui/core';
|
|
|
|
|
import clsx from 'clsx';
|
2021-02-02 14:47:58 +05:30
|
|
|
import {useDelayDebounce} from 'sources/custom_hooks';
|
|
|
|
|
import {onlineHelpSearch} from './online_help';
|
|
|
|
|
import {menuSearch} from './menuitems_help';
|
|
|
|
|
import $ from 'jquery';
|
|
|
|
|
import gettext from 'sources/gettext';
|
2022-09-10 14:00:22 +05:30
|
|
|
import PropTypes from 'prop-types';
|
2022-12-06 18:16:36 +05:30
|
|
|
import pgAdmin from 'sources/pgadmin';
|
|
|
|
|
import { getBrowser } from '../../../../static/js/utils';
|
2022-09-10 14:00:22 +05:30
|
|
|
|
|
|
|
|
function HelpArticleContents({isHelpLoading, isMenuLoading, helpSearchResult}) {
|
|
|
|
|
return (isHelpLoading && !(isMenuLoading??true)) ? (
|
|
|
|
|
<div>
|
|
|
|
|
<div className='help-groups'>
|
|
|
|
|
<span className='fa fa-question-circle'></span>
|
|
|
|
|
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 <span className='fas fa-external-link-alt' ></span></a> : ''
|
|
|
|
|
}
|
|
|
|
|
</div>
|
|
|
|
|
<div className='pad-12'><div className="search-icon">{gettext('Searching...')}</div></div>
|
|
|
|
|
</div>) : '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HelpArticleContents.propTypes = {
|
|
|
|
|
helpSearchResult: PropTypes.object,
|
|
|
|
|
isHelpLoading: PropTypes.bool,
|
|
|
|
|
isMenuLoading: PropTypes.bool
|
|
|
|
|
};
|
2021-02-02 14:47:58 +05:30
|
|
|
|
2022-12-06 18:16:36 +05:30
|
|
|
const useModalStyles = makeStyles(() => ({
|
|
|
|
|
setTop: {
|
|
|
|
|
marginTop: '-20px',
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
export function Search({closeModal}) {
|
|
|
|
|
let {name: browser} = getBrowser();
|
|
|
|
|
const classes = useModalStyles();
|
2021-02-02 14:47:58 +05:30
|
|
|
const wrapperRef = useRef(null);
|
2022-12-06 18:16:36 +05:30
|
|
|
const firstEleRef = useRef();
|
2021-02-02 14:47:58 +05:30
|
|
|
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: [],
|
|
|
|
|
}));
|
2022-12-06 18:16:36 +05:30
|
|
|
|
2021-02-02 14:47:58 +05:30
|
|
|
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 = [];
|
2022-12-06 18:16:36 +05:30
|
|
|
items.forEach((i) => {
|
|
|
|
|
menuItemsHtmlElement.push(
|
|
|
|
|
<li key={ 'li-menu-' + i }><a tabIndex='0' id={ 'li-menu-' + i.label } href={'#'} className={ (i.is_disabled ? 'dropdown-item menu-groups-a disabled':'dropdown-item menu-groups-a')} key={ 'menu-' + i.label } onClick={
|
|
|
|
|
() => {
|
|
|
|
|
closeModal();
|
|
|
|
|
if(browser == 'Nwjs') {
|
|
|
|
|
i.callback();
|
|
|
|
|
} else {
|
|
|
|
|
// Some callbacks registered in 'callbacks' check and call specifiec callback function
|
|
|
|
|
if (i.module && 'callbacks' in i.module && i.module.callbacks[i.callback]) {
|
|
|
|
|
i.module.callbacks[i.callback].apply(i.module, [i.data, pgAdmin.Browser.tree.selected()]);
|
|
|
|
|
} else if (i.module && i.module[i.callback]) {
|
|
|
|
|
i.module[i.callback].apply(i.module, [i.data, pgAdmin.Browser.tree.selected()]);
|
|
|
|
|
} else if (i.callback) {
|
|
|
|
|
i.callback(i);
|
|
|
|
|
} else {
|
|
|
|
|
window.open(i.url);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}>
|
|
|
|
|
{i.label}
|
|
|
|
|
<span key={ 'menu-span-' + i.label }>{i.path}</span>
|
|
|
|
|
</a>
|
|
|
|
|
</li>);
|
|
|
|
|
});
|
2021-02-02 14:47:58 +05:30
|
|
|
$('[data-toggle="tooltip"]').tooltip();
|
|
|
|
|
return menuItemsHtmlElement;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2022-09-10 14:00:22 +05:30
|
|
|
const showLoader = (loading) => {
|
|
|
|
|
return loading ? <div className='pad-12'><div className="search-icon">{gettext('Searching...')}</div></div> : '';
|
|
|
|
|
};
|
|
|
|
|
|
2022-12-06 18:16:36 +05:30
|
|
|
useEffect(() => {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
firstEleRef.current && firstEleRef.current.focus();
|
|
|
|
|
}, 350);
|
|
|
|
|
}, [firstEleRef.current]);
|
|
|
|
|
|
2021-02-02 14:47:58 +05:30
|
|
|
return (
|
|
|
|
|
<div id='quick-search-container' onClick={setSearchTerm}></div>,
|
2022-12-06 18:16:36 +05:30
|
|
|
<ul id='quick-search-container' ref={wrapperRef} className={clsx('test', classes.setTop)} role="menu">
|
2021-02-02 14:47:58 +05:30
|
|
|
<li>
|
|
|
|
|
<ul id='myDropdown'>
|
|
|
|
|
<li className='dropdown-item-input'>
|
2022-12-06 18:16:36 +05:30
|
|
|
<input ref={firstEleRef} tabIndex='0' autoFocus type='text' autoComplete='off' className='form-control live-search-field'
|
2021-02-02 14:47:58 +05:30
|
|
|
aria-label='live-search-field' id='live-search-field' placeholder={gettext('Quick Search')} onChange={(e) => {onInputValueChange(e.target.value);} } />
|
|
|
|
|
</li>
|
|
|
|
|
<div style={{marginBottom:0}}>
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
|
|
{ isShowMinLengthMsg
|
|
|
|
|
? (<div className='pad-12 no-results'>
|
|
|
|
|
<span className='fa fa-info-circle'></span>
|
|
|
|
|
Please enter minimum 3 characters to search
|
|
|
|
|
</div>)
|
|
|
|
|
:''}
|
|
|
|
|
<div >
|
2022-09-09 15:23:18 +05:30
|
|
|
{ (menuSearchResult.fetched && !(isMenuLoading??true) ) ?
|
2021-02-02 14:47:58 +05:30
|
|
|
<div>
|
|
|
|
|
<div className='menu-groups'>
|
|
|
|
|
<span className='fa fa-window-maximize'></span> {gettext('MENU ITEMS')} ({menuSearchResult.data.length})
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{refactorMenuItems(menuSearchResult.data)}
|
2022-09-10 14:00:22 +05:30
|
|
|
</div> : showLoader(isMenuLoading)}
|
2021-02-02 14:47:58 +05:30
|
|
|
|
2022-09-09 15:23:18 +05:30
|
|
|
{(menuSearchResult.data.length == 0 && menuSearchResult.fetched && !(isMenuLoading??true)) ? (<div className='pad-12 no-results'><span className='fa fa-info-circle'></span> {gettext('No search results')}</div>):''}
|
2021-02-02 14:47:58 +05:30
|
|
|
|
2022-09-09 15:23:18 +05:30
|
|
|
{ (helpSearchResult.fetched && !(isHelpLoading??true)) ?
|
2021-02-02 14:47:58 +05:30
|
|
|
<div>
|
|
|
|
|
<div className='help-groups'>
|
|
|
|
|
<span className='fa fa-question-circle'></span> {gettext('HELP ARTICLES')} {Object.keys(helpSearchResult.data).length > 10 ?
|
|
|
|
|
<span>(10 of {Object.keys(helpSearchResult.data).length} )
|
|
|
|
|
</span>:
|
|
|
|
|
'(' + Object.keys(helpSearchResult.data).length + ')'}
|
|
|
|
|
{ !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')} <span className='fas fa-external-link-alt' ></span></a> : ''}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{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></li>; }
|
|
|
|
|
})}
|
|
|
|
|
|
|
|
|
|
{(Object.keys(helpSearchResult.data).length == 0) ? (<div className='pad-12 no-results'><span className='fa fa-info-circle'></span> {gettext('No search results')}</div>):''}
|
2022-09-10 14:00:22 +05:30
|
|
|
|
|
|
|
|
</div> : <HelpArticleContents isHelpLoading={isHelpLoading} isMenuLoading={isMenuLoading} helpSearchResult={helpSearchResult} /> }
|
2021-02-02 14:47:58 +05:30
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</ul>
|
|
|
|
|
</li>
|
|
|
|
|
<div id='quick-search-iframe-container' />
|
|
|
|
|
</ul>
|
|
|
|
|
);
|
|
|
|
|
}
|
2022-12-06 18:16:36 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
Search.propTypes = {
|
|
|
|
|
closeModal: PropTypes.func
|
|
|
|
|
};
|