Focus on search input when opening search popup

This commit is contained in:
DrMint 2023-05-11 00:41:36 +02:00
parent a8960d67ed
commit 5d2fe252ec
3 changed files with 58 additions and 43 deletions

View File

@ -11,6 +11,7 @@ import { Button } from "components/Inputs/Button";
*/
interface Props {
onOpen?: () => void;
onCloseRequest?: () => void;
isVisible: boolean;
children: React.ReactNode;
@ -23,6 +24,7 @@ interface Props {
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
export const Popup = ({
onOpen,
onCloseRequest,
isVisible,
children,
@ -47,13 +49,18 @@ export const Popup = ({
if (isVisible) {
setHidden(false);
// We delay the visiblity of the element so that the opening animation is played
timeouts.push(setTimeout(() => setActuallyVisible(true), 100));
timeouts.push(
setTimeout(() => {
setActuallyVisible(true);
onOpen?.();
}, 100)
);
} else {
setActuallyVisible(false);
timeouts.push(setTimeout(() => setHidden(true), 600));
}
return () => timeouts.forEach(clearTimeout);
}, [isVisible]);
}, [isVisible, onOpen]);
return isHidden ? (
<></>

View File

@ -1,3 +1,4 @@
import { forwardRef } from "react";
import { Ico } from "components/Ico";
import { cIf, cJoin } from "helpers/className";
import { isDefinedAndNotEmpty } from "helpers/asserts";
@ -18,16 +19,11 @@ interface Props {
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
export const TextInput = ({
value,
onChange,
className,
name,
placeholder,
disabled = false,
}: Props): JSX.Element => (
export const TextInput = forwardRef<HTMLInputElement, Props>(
({ value, onChange, className, name, placeholder, disabled = false }, ref) => (
<div className={cJoin("relative", className)}>
<input
ref={ref}
className="w-full"
type="text"
name={name}
@ -48,4 +44,6 @@ export const TextInput = ({
</div>
)}
</div>
)
);
TextInput.displayName = "TextInput";

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useCallback, useRef, useState } from "react";
import { MaterialSymbol } from "material-symbols";
import { Popup } from "components/Containers/Popup";
import { sendAnalytics } from "helpers/analytics";
@ -55,13 +55,13 @@ export const SearchPopup = (): JSX.Element => {
const { format } = useFormat();
const [multiResult, setMultiResult] = useState<MultiResult>({});
useEffect(() => {
const fetchSearchResults = useCallback((q: string) => {
const fetchMultiResult = async () => {
const searchResults = (
await meiliMultiSearch([
{
indexUid: MeiliIndices.LIBRARY_ITEM,
q: query,
q,
limit: SEARCH_LIMIT,
attributesToRetrieve: [
"title",
@ -80,7 +80,7 @@ export const SearchPopup = (): JSX.Element => {
},
{
indexUid: MeiliIndices.CONTENT,
q: query,
q,
limit: SEARCH_LIMIT,
attributesToRetrieve: ["translations", "id", "slug", "categories", "type", "thumbnail"],
attributesToHighlight: ["translations"],
@ -88,7 +88,7 @@ export const SearchPopup = (): JSX.Element => {
},
{
indexUid: MeiliIndices.VIDEOS,
q: query,
q,
limit: SEARCH_LIMIT,
attributesToRetrieve: [
"title",
@ -104,7 +104,7 @@ export const SearchPopup = (): JSX.Element => {
},
{
indexUid: MeiliIndices.POST,
q: query,
q,
limit: SEARCH_LIMIT,
attributesToRetrieve: ["translations", "thumbnail", "slug", "date", "categories"],
attributesToHighlight: [
@ -117,7 +117,7 @@ export const SearchPopup = (): JSX.Element => {
},
{
indexUid: MeiliIndices.WEAPON,
q: query,
q,
limit: SEARCH_LIMIT,
attributesToHighlight: ["translations.description", "translations.names"],
attributesToCrop: ["translations.description"],
@ -125,7 +125,7 @@ export const SearchPopup = (): JSX.Element => {
},
{
indexUid: MeiliIndices.WIKI_PAGE,
q: query,
q,
limit: SEARCH_LIMIT,
attributesToHighlight: [
"translations.title",
@ -184,12 +184,16 @@ export const SearchPopup = (): JSX.Element => {
setMultiResult(result);
};
if (query === "") {
if (q === "") {
setMultiResult({});
} else {
fetchMultiResult();
}
}, [query]);
setQuery(q);
}, []);
const searchInputRef = useRef<HTMLInputElement>(null);
return (
<Popup
@ -198,12 +202,18 @@ export const SearchPopup = (): JSX.Element => {
setSearchOpened(false);
sendAnalytics("Search", "Close search");
}}
onOpen={() => searchInputRef.current?.focus()}
fillViewport>
<h2 className="inline-flex place-items-center gap-2 text-2xl">
<Ico icon="search" isFilled />
{format("search")}
</h2>
<TextInput onChange={setQuery} value={query} placeholder={format("search_title")} />
<TextInput
ref={searchInputRef}
onChange={fetchSearchResults}
value={query}
placeholder={format("search_title")}
/>
<div className="flex w-full flex-wrap gap-12 gap-x-16">
{isDefined(multiResult.libraryItems) && (