|
@@ -1,15 +1,17 @@
|
|
|
import React, {
|
|
import React, {
|
|
|
- FC, useState, useCallback,
|
|
|
|
|
|
|
+ FC, useState, useCallback, useRef,
|
|
|
} from 'react';
|
|
} from 'react';
|
|
|
import { useTranslation } from 'react-i18next';
|
|
import { useTranslation } from 'react-i18next';
|
|
|
import assert from 'assert';
|
|
import assert from 'assert';
|
|
|
|
|
|
|
|
import AppContainer from '~/client/services/AppContainer';
|
|
import AppContainer from '~/client/services/AppContainer';
|
|
|
import { IPageSearchResultData } from '~/interfaces/search';
|
|
import { IPageSearchResultData } from '~/interfaces/search';
|
|
|
|
|
+import { IFocusable } from '~/client/interfaces/focusable';
|
|
|
|
|
|
|
|
import { withUnstatedContainers } from '../UnstatedUtils';
|
|
import { withUnstatedContainers } from '../UnstatedUtils';
|
|
|
|
|
|
|
|
import SearchForm from '../SearchForm';
|
|
import SearchForm from '../SearchForm';
|
|
|
|
|
+import { useGlobalSearchFormRef } from '~/stores/ui';
|
|
|
|
|
|
|
|
|
|
|
|
|
type Props = {
|
|
type Props = {
|
|
@@ -22,8 +24,13 @@ const GlobalSearch: FC<Props> = (props: Props) => {
|
|
|
const { appContainer, dropup } = props;
|
|
const { appContainer, dropup } = props;
|
|
|
const { t } = useTranslation();
|
|
const { t } = useTranslation();
|
|
|
|
|
|
|
|
|
|
+ const globalSearchFormRef = useRef<IFocusable>(null);
|
|
|
|
|
+
|
|
|
|
|
+ useGlobalSearchFormRef(globalSearchFormRef);
|
|
|
|
|
+
|
|
|
const [text, setText] = useState('');
|
|
const [text, setText] = useState('');
|
|
|
const [isScopeChildren, setScopeChildren] = useState<boolean>(appContainer.getConfig().isSearchScopeChildrenAsDefault);
|
|
const [isScopeChildren, setScopeChildren] = useState<boolean>(appContainer.getConfig().isSearchScopeChildrenAsDefault);
|
|
|
|
|
+ const [isFocused, setFocused] = useState<boolean>(false);
|
|
|
|
|
|
|
|
const gotoPage = useCallback((data: IPageSearchResultData[]) => {
|
|
const gotoPage = useCallback((data: IPageSearchResultData[]) => {
|
|
|
assert(data.length > 0);
|
|
assert(data.length > 0);
|
|
@@ -56,6 +63,8 @@ const GlobalSearch: FC<Props> = (props: Props) => {
|
|
|
|
|
|
|
|
const isSearchServiceReachable = appContainer.getConfig().isSearchServiceReachable;
|
|
const isSearchServiceReachable = appContainer.getConfig().isSearchServiceReachable;
|
|
|
|
|
|
|
|
|
|
+ const isIndicatorShown = !isFocused && (text.length === 0);
|
|
|
|
|
+
|
|
|
return (
|
|
return (
|
|
|
<div className={`form-group mb-0 d-print-none ${isSearchServiceReachable ? '' : 'has-error'}`}>
|
|
<div className={`form-group mb-0 d-print-none ${isSearchServiceReachable ? '' : 'has-error'}`}>
|
|
|
<div className="input-group flex-nowrap">
|
|
<div className="input-group flex-nowrap">
|
|
@@ -64,21 +73,43 @@ const GlobalSearch: FC<Props> = (props: Props) => {
|
|
|
{scopeLabel}
|
|
{scopeLabel}
|
|
|
</button>
|
|
</button>
|
|
|
<div className="dropdown-menu">
|
|
<div className="dropdown-menu">
|
|
|
- <button className="dropdown-item" type="button" onClick={() => setScopeChildren(false)}>
|
|
|
|
|
|
|
+ <button
|
|
|
|
|
+ className="dropdown-item"
|
|
|
|
|
+ type="button"
|
|
|
|
|
+ onClick={() => {
|
|
|
|
|
+ setScopeChildren(false);
|
|
|
|
|
+ globalSearchFormRef.current?.focus();
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
{ t('header_search_box.item_label.All pages') }
|
|
{ t('header_search_box.item_label.All pages') }
|
|
|
</button>
|
|
</button>
|
|
|
- <button className="dropdown-item" type="button" onClick={() => setScopeChildren(true)}>
|
|
|
|
|
|
|
+ <button
|
|
|
|
|
+ className="dropdown-item"
|
|
|
|
|
+ type="button"
|
|
|
|
|
+ onClick={() => {
|
|
|
|
|
+ setScopeChildren(true);
|
|
|
|
|
+ globalSearchFormRef.current?.focus();
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
{ t('header_search_box.item_label.This tree') }
|
|
{ t('header_search_box.item_label.This tree') }
|
|
|
</button>
|
|
</button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
<SearchForm
|
|
<SearchForm
|
|
|
|
|
+ ref={globalSearchFormRef}
|
|
|
isSearchServiceReachable={isSearchServiceReachable}
|
|
isSearchServiceReachable={isSearchServiceReachable}
|
|
|
dropup={dropup}
|
|
dropup={dropup}
|
|
|
onChange={gotoPage}
|
|
onChange={gotoPage}
|
|
|
|
|
+ onBlur={() => setFocused(false)}
|
|
|
|
|
+ onFocus={() => setFocused(true)}
|
|
|
onInputChange={text => setText(text)}
|
|
onInputChange={text => setText(text)}
|
|
|
onSubmit={search}
|
|
onSubmit={search}
|
|
|
/>
|
|
/>
|
|
|
|
|
+ { isIndicatorShown && (
|
|
|
|
|
+ <span className="grw-shortcut-key-indicator">
|
|
|
|
|
+ <code className="bg-transparent text-muted">/</code>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ ) }
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
);
|
|
);
|