|
@@ -7,6 +7,7 @@ import React, {
|
|
|
} from 'react';
|
|
} from 'react';
|
|
|
|
|
|
|
|
import { UserPicture, PageListMeta, PagePathLabel } from '@growi/ui/dist/components';
|
|
import { UserPicture, PageListMeta, PagePathLabel } from '@growi/ui/dist/components';
|
|
|
|
|
+import type { TypeaheadRef } from 'react-bootstrap-typeahead';
|
|
|
import { AsyncTypeahead, Menu, MenuItem } from 'react-bootstrap-typeahead';
|
|
import { AsyncTypeahead, Menu, MenuItem } from 'react-bootstrap-typeahead';
|
|
|
|
|
|
|
|
import type { IFocusable } from '~/client/interfaces/focusable';
|
|
import type { IFocusable } from '~/client/interfaces/focusable';
|
|
@@ -46,15 +47,6 @@ type Props = TypeaheadProps & {
|
|
|
helpElement?: React.ReactNode,
|
|
helpElement?: React.ReactNode,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-// see https://github.com/ericgio/react-bootstrap-typeahead/issues/266#issuecomment-414987723
|
|
|
|
|
-type TypeaheadInstance = {
|
|
|
|
|
- setState(input: { text: string | undefined; }): void;
|
|
|
|
|
- clear: () => void,
|
|
|
|
|
- focus: () => void,
|
|
|
|
|
- toggleMenu: () => void,
|
|
|
|
|
- state: { selected: IPageWithSearchMeta[] }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
const SearchTypeahead: ForwardRefRenderFunction<IFocusable, Props> = (props: Props, ref) => {
|
|
const SearchTypeahead: ForwardRefRenderFunction<IFocusable, Props> = (props: Props, ref) => {
|
|
|
const {
|
|
const {
|
|
|
onSearchError, onSearch, onInputChange, onChange, onSubmit,
|
|
onSearchError, onSearch, onInputChange, onChange, onSubmit,
|
|
@@ -64,7 +56,7 @@ const SearchTypeahead: ForwardRefRenderFunction<IFocusable, Props> = (props: Pro
|
|
|
|
|
|
|
|
const [input, setInput] = useState(keywordOnInit);
|
|
const [input, setInput] = useState(keywordOnInit);
|
|
|
const [searchKeyword, setSearchKeyword] = useState('');
|
|
const [searchKeyword, setSearchKeyword] = useState('');
|
|
|
- const [isForcused, setFocused] = useState(false);
|
|
|
|
|
|
|
+ const [isFocused, setFocused] = useState(false);
|
|
|
|
|
|
|
|
const { data: searchResult, error: searchError, isLoading } = useSWRxSearch(
|
|
const { data: searchResult, error: searchError, isLoading } = useSWRxSearch(
|
|
|
disableIncrementalSearch ? null : searchKeyword,
|
|
disableIncrementalSearch ? null : searchKeyword,
|
|
@@ -72,7 +64,7 @@ const SearchTypeahead: ForwardRefRenderFunction<IFocusable, Props> = (props: Pro
|
|
|
{ limit: 10 },
|
|
{ limit: 10 },
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
- const typeaheadRef = useRef<TypeaheadInstance>(null);
|
|
|
|
|
|
|
+ const typeaheadRef = useRef<TypeaheadRef>(null);
|
|
|
|
|
|
|
|
const focusToTypeahead = () => {
|
|
const focusToTypeahead = () => {
|
|
|
const instance = typeaheadRef.current;
|
|
const instance = typeaheadRef.current;
|
|
@@ -144,14 +136,18 @@ const SearchTypeahead: ForwardRefRenderFunction<IFocusable, Props> = (props: Pro
|
|
|
if (selectedItems.length > 0) {
|
|
if (selectedItems.length > 0) {
|
|
|
setInput(selectedItems[0].data.path);
|
|
setInput(selectedItems[0].data.path);
|
|
|
|
|
|
|
|
|
|
+ if (onInputChange != null) {
|
|
|
|
|
+ onInputChange(selectedItems[0].data.path);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if (onChange != null) {
|
|
if (onChange != null) {
|
|
|
onChange(selectedItems);
|
|
onChange(selectedItems);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- }, [onChange]);
|
|
|
|
|
|
|
+ }, [onChange, onInputChange]);
|
|
|
|
|
|
|
|
const keyDownHandler = useCallback((event: KeyboardEvent) => {
|
|
const keyDownHandler = useCallback((event: KeyboardEvent) => {
|
|
|
- if (event.keyCode === 13) { // Enter key
|
|
|
|
|
|
|
+ if (event.key === 'Enter') {
|
|
|
if (onSubmit != null && input != null && input.length > 0) {
|
|
if (onSubmit != null && input != null && input.length > 0) {
|
|
|
// schedule to submit with 100ms delay
|
|
// schedule to submit with 100ms delay
|
|
|
timeoutIdRef.current = setTimeout(() => onSubmit(input), DELAY_FOR_SUBMISSION);
|
|
timeoutIdRef.current = setTimeout(() => onSubmit(input), DELAY_FOR_SUBMISSION);
|
|
@@ -171,19 +167,20 @@ const SearchTypeahead: ForwardRefRenderFunction<IFocusable, Props> = (props: Pro
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
// update input with Next Link
|
|
// update input with Next Link
|
|
|
// update input workaround. see: https://github.com/ericgio/react-bootstrap-typeahead/issues/266#issuecomment-414987723
|
|
// update input workaround. see: https://github.com/ericgio/react-bootstrap-typeahead/issues/266#issuecomment-414987723
|
|
|
- if (typeaheadRef.current != null) {
|
|
|
|
|
|
|
+ if (typeaheadRef.current != null && keywordOnInit != null) {
|
|
|
typeaheadRef.current.setState({
|
|
typeaheadRef.current.setState({
|
|
|
text: keywordOnInit,
|
|
text: keywordOnInit,
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
}, [keywordOnInit]);
|
|
}, [keywordOnInit]);
|
|
|
|
|
|
|
|
- const labelKey = useCallback((option?: IPageWithSearchMeta) => {
|
|
|
|
|
- return option?.data.path ?? '';
|
|
|
|
|
|
|
+
|
|
|
|
|
+ const labelKey = useCallback((option: IPageWithSearchMeta) => {
|
|
|
|
|
+ return option.data.path ?? '';
|
|
|
}, []);
|
|
}, []);
|
|
|
|
|
|
|
|
const renderMenu = useCallback((options: IPageWithSearchMeta[], menuProps) => {
|
|
const renderMenu = useCallback((options: IPageWithSearchMeta[], menuProps) => {
|
|
|
- if (!isForcused) {
|
|
|
|
|
|
|
+ if (!isFocused) {
|
|
|
return <></>;
|
|
return <></>;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -219,7 +216,7 @@ const SearchTypeahead: ForwardRefRenderFunction<IFocusable, Props> = (props: Pro
|
|
|
))}
|
|
))}
|
|
|
</Menu>
|
|
</Menu>
|
|
|
);
|
|
);
|
|
|
- }, [disableIncrementalSearch, helpElement, input, isForcused]);
|
|
|
|
|
|
|
+ }, [disableIncrementalSearch, helpElement, input, isFocused]);
|
|
|
|
|
|
|
|
const isOpenAlways = helpElement != null;
|
|
const isOpenAlways = helpElement != null;
|
|
|
|
|
|
|
@@ -228,12 +225,12 @@ const SearchTypeahead: ForwardRefRenderFunction<IFocusable, Props> = (props: Pro
|
|
|
<AsyncTypeahead
|
|
<AsyncTypeahead
|
|
|
{...props}
|
|
{...props}
|
|
|
id="search-typeahead-asynctypeahead"
|
|
id="search-typeahead-asynctypeahead"
|
|
|
- // ref={typeaheadRef}
|
|
|
|
|
|
|
+ ref={typeaheadRef}
|
|
|
delay={400}
|
|
delay={400}
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
inputProps={{ autoComplete: 'off', ...(inputProps as any ?? {}) }}
|
|
inputProps={{ autoComplete: 'off', ...(inputProps as any ?? {}) }}
|
|
|
isLoading={isLoading}
|
|
isLoading={isLoading}
|
|
|
- // labelKey={labelKey}
|
|
|
|
|
|
|
+ labelKey={labelKey}
|
|
|
defaultInputValue={keywordOnInit}
|
|
defaultInputValue={keywordOnInit}
|
|
|
options={searchResult?.data ?? []} // Search result (Some page names)
|
|
options={searchResult?.data ?? []} // Search result (Some page names)
|
|
|
align="left"
|
|
align="left"
|