145 lines
4.4 KiB
TypeScript
145 lines
4.4 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState } from "react";
|
|
import { Search } from "lucide-react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from "@/components/ui/dialog";
|
|
import { useRouter, useSearchParams, usePathname } from "next/navigation";
|
|
import { SearchIcon } from "@/components/icons";
|
|
|
|
interface SearchBarProps {
|
|
isMobile: boolean;
|
|
searchPlaceholder: string;
|
|
isOpen?: boolean;
|
|
onClose?: () => void;
|
|
className?: string;
|
|
locale?: string;
|
|
}
|
|
|
|
export default function SearchBar({
|
|
isMobile,
|
|
searchPlaceholder,
|
|
isOpen,
|
|
onClose,
|
|
className = "",
|
|
locale = "ru",
|
|
}: SearchBarProps) {
|
|
const router = useRouter();
|
|
const searchParams = useSearchParams();
|
|
const pathname = usePathname();
|
|
const [searchValue, setSearchValue] = useState("");
|
|
|
|
React.useEffect(() => {
|
|
// Sync URL param to input when URL changes
|
|
if (pathname?.includes("/search")) {
|
|
const q = searchParams?.get("q") || "";
|
|
if (q !== searchValue.trim()) {
|
|
setSearchValue(q);
|
|
}
|
|
} else {
|
|
setSearchValue("");
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [searchParams, pathname]);
|
|
|
|
React.useEffect(() => {
|
|
// Debounce updates to the URL query when typing on search page
|
|
if (pathname?.includes("/search")) {
|
|
const delayDebounceFn = setTimeout(() => {
|
|
const q = searchParams?.get("q") || "";
|
|
const curVal = searchValue.trim();
|
|
if (curVal !== q && curVal !== "") {
|
|
router.push(`/${locale}/search?q=${encodeURIComponent(curVal)}`);
|
|
} else if (curVal === "" && q !== "") {
|
|
router.push(`/${locale}/search`);
|
|
}
|
|
}, 500);
|
|
|
|
return () => clearTimeout(delayDebounceFn);
|
|
}
|
|
}, [searchValue, pathname, router, locale, searchParams]);
|
|
|
|
const performSearch = () => {
|
|
if (searchValue.trim()) {
|
|
router.push(
|
|
`/${locale}/search?q=${encodeURIComponent(searchValue.trim())}`,
|
|
);
|
|
if (onClose) onClose();
|
|
}
|
|
};
|
|
|
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
if (e.key === "Enter") {
|
|
performSearch();
|
|
}
|
|
};
|
|
|
|
const handleSearch = (value: string) => {
|
|
setSearchValue(value);
|
|
};
|
|
|
|
if (isMobile) {
|
|
return (
|
|
<Dialog open={isOpen} onOpenChange={onClose}>
|
|
<DialogContent className="top-4 translate-y-0 rounded-lg border-gray-200">
|
|
<DialogHeader>
|
|
<DialogTitle className="text-xl font-bold">
|
|
{searchPlaceholder}
|
|
</DialogTitle>
|
|
</DialogHeader>
|
|
<div className="relative">
|
|
<div className="relative">
|
|
<Input
|
|
type="text"
|
|
placeholder={searchPlaceholder}
|
|
value={searchValue}
|
|
onChange={(e) => handleSearch(e.target.value)}
|
|
onKeyDown={handleKeyDown}
|
|
className="h-12 rounded-lg pl-12 pr-10 border-gray-200 focus:border-gray-900 focus-visible:border-gray-900 focus-visible:ring-0 transition-colors"
|
|
autoFocus
|
|
/>
|
|
<Search
|
|
className="absolute left-4 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400 cursor-pointer"
|
|
onClick={performSearch}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className={`bg-gray-900 rounded-lg flex items-center relative shadow-sm hover:shadow-md transition-shadow duration-200 ${className}`}
|
|
>
|
|
<div className="w-full relative">
|
|
<div className="relative">
|
|
<Input
|
|
type="text"
|
|
placeholder={searchPlaceholder}
|
|
value={searchValue}
|
|
onChange={(e) => handleSearch(e.target.value)}
|
|
onKeyDown={handleKeyDown}
|
|
className="border w-full rounded-lg h-11 border-gray-900 bg-white pl-12 pr-4 focus-visible:ring-2 focus-visible:ring-gray-300 transition-all"
|
|
/>
|
|
<Search className="absolute left-4 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400 pointer-events-none" />
|
|
</div>
|
|
</div>
|
|
<Button
|
|
size="icon"
|
|
onClick={performSearch}
|
|
className="h-11 w-11 hover:bg-gray-800 cursor-pointer bg-transparent flex items-center mr-1 text-white rounded-lg transition-colors"
|
|
>
|
|
<SearchIcon />
|
|
</Button>
|
|
</div>
|
|
);
|
|
}
|