80 lines
2.6 KiB
TypeScript
80 lines
2.6 KiB
TypeScript
import { Button } from '@/Components/ui/button';
|
|
import { CardHeader } from '@/Components/ui/card';
|
|
import { Input } from '@/Components/ui/input';
|
|
import { Search } from 'lucide-react';
|
|
import { FormEvent, ReactNode } from 'react';
|
|
|
|
interface DataTableToolbarProps {
|
|
searchValue?: string;
|
|
searchPlaceholder?: string;
|
|
onSearchChange?: (value: string) => void;
|
|
onSearchSubmit?: (e: FormEvent) => void;
|
|
filters?: ReactNode;
|
|
actions?: ReactNode;
|
|
children?: ReactNode;
|
|
}
|
|
|
|
export function DataTableToolbar({
|
|
searchValue,
|
|
searchPlaceholder = 'Search...',
|
|
onSearchChange,
|
|
onSearchSubmit,
|
|
filters,
|
|
actions,
|
|
children,
|
|
}: DataTableToolbarProps) {
|
|
const hasSearch = searchValue !== undefined && onSearchChange;
|
|
|
|
if (children) {
|
|
return (
|
|
<CardHeader>
|
|
<div className="flex flex-wrap items-center gap-3">
|
|
{children}
|
|
{actions && (
|
|
<div className="ml-auto flex items-center gap-2">
|
|
{actions}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</CardHeader>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<CardHeader>
|
|
<div className="flex flex-wrap items-center gap-3">
|
|
{hasSearch && (
|
|
<form
|
|
onSubmit={(e) => {
|
|
e.preventDefault();
|
|
onSearchSubmit?.(e);
|
|
}}
|
|
className="flex flex-1 min-w-[200px] gap-2"
|
|
>
|
|
<div className="relative flex-1">
|
|
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400" />
|
|
<Input
|
|
placeholder={searchPlaceholder}
|
|
value={searchValue}
|
|
onChange={(e) => onSearchChange(e.target.value)}
|
|
className="pl-10"
|
|
/>
|
|
</div>
|
|
{onSearchSubmit && (
|
|
<Button type="submit" variant="secondary" size="sm">
|
|
Search
|
|
</Button>
|
|
)}
|
|
</form>
|
|
)}
|
|
{filters}
|
|
{actions && (
|
|
<div className="ml-auto flex items-center gap-2">
|
|
{actions}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</CardHeader>
|
|
);
|
|
}
|