111 lines
2.9 KiB
TypeScript
111 lines
2.9 KiB
TypeScript
|
import { twMerge } from "tailwind-merge";
|
||
|
import Input from "../form/Input";
|
||
|
import Button from "../layout/Button";
|
||
|
import Row from "../layout/Row";
|
||
|
import { Search as SearchIcon } from "lucide-react";
|
||
|
import React, {
|
||
|
DetailedHTMLProps,
|
||
|
InputHTMLAttributes,
|
||
|
TextareaHTMLAttributes,
|
||
|
} from "react";
|
||
|
|
||
|
let timeout: any;
|
||
|
|
||
|
export type SearchProps = DetailedHTMLProps<
|
||
|
React.HTMLAttributes<HTMLDivElement>,
|
||
|
HTMLDivElement
|
||
|
> & {
|
||
|
dispatch?: (value?: string) => void;
|
||
|
delay?: number;
|
||
|
inputProps?: DetailedHTMLProps<
|
||
|
InputHTMLAttributes<HTMLInputElement>,
|
||
|
HTMLInputElement
|
||
|
> &
|
||
|
DetailedHTMLProps<
|
||
|
TextareaHTMLAttributes<HTMLTextAreaElement>,
|
||
|
HTMLTextAreaElement
|
||
|
>;
|
||
|
buttonProps?: DetailedHTMLProps<
|
||
|
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||
|
HTMLButtonElement
|
||
|
>;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* # Search Component
|
||
|
* @className_wrapper twui-search-wrapper
|
||
|
* @className_circle twui-search-input
|
||
|
* @className_circle twui-search-button
|
||
|
*/
|
||
|
export default function Search({
|
||
|
dispatch,
|
||
|
delay = 500,
|
||
|
inputProps,
|
||
|
buttonProps,
|
||
|
...props
|
||
|
}: SearchProps) {
|
||
|
const [input, setInput] = React.useState("");
|
||
|
|
||
|
React.useEffect(() => {
|
||
|
clearTimeout(timeout);
|
||
|
|
||
|
timeout = setTimeout(() => {
|
||
|
dispatch?.(input);
|
||
|
}, delay);
|
||
|
}, [input]);
|
||
|
|
||
|
const inputRef = React.useRef<HTMLInputElement>();
|
||
|
|
||
|
React.useEffect(() => {
|
||
|
if (props.autoFocus) {
|
||
|
inputRef.current?.focus();
|
||
|
}
|
||
|
}, []);
|
||
|
|
||
|
return (
|
||
|
<Row
|
||
|
{...props}
|
||
|
className={twMerge(
|
||
|
"relative xl:flex-nowrap items-stretch gap-0",
|
||
|
"twui-search-wrapper",
|
||
|
props?.className
|
||
|
)}
|
||
|
>
|
||
|
<Input
|
||
|
type="search"
|
||
|
placeholder="Search"
|
||
|
{...inputProps}
|
||
|
value={input}
|
||
|
onChange={(e) => setInput(e.target.value)}
|
||
|
className={twMerge(
|
||
|
"rounded-r-none",
|
||
|
"twui-search-input",
|
||
|
inputProps?.className
|
||
|
)}
|
||
|
wrapperProps={{
|
||
|
className: "rounded-r-none",
|
||
|
}}
|
||
|
componentRef={inputRef}
|
||
|
/>
|
||
|
<Button
|
||
|
{...buttonProps}
|
||
|
variant="outlined"
|
||
|
color="gray"
|
||
|
className={twMerge(
|
||
|
"rounded-l-none my-[1px]",
|
||
|
"twui-search-button",
|
||
|
buttonProps?.className
|
||
|
)}
|
||
|
onClick={() => {
|
||
|
dispatch?.(input);
|
||
|
}}
|
||
|
>
|
||
|
<SearchIcon
|
||
|
className="text-slate-800 dark:text-white"
|
||
|
size={20}
|
||
|
/>
|
||
|
</Button>
|
||
|
</Row>
|
||
|
);
|
||
|
}
|