add filter sheet

This commit is contained in:
Abhimanyu Yadav
2025-05-19 16:34:26 +05:30
parent 4273be59ba
commit 773e1488bf
2 changed files with 237 additions and 9 deletions

View File

@@ -0,0 +1,142 @@
// Used v0 for this component, so review it very carefully
import FilterChip from "../FilterChip";
import { useState, useEffect } from "react";
import { Button } from "@/components/ui/button";
import { X } from "lucide-react";
import { cn } from "@/lib/utils";
import { Separator } from "@/components/ui/separator";
import { CategoryKey, Filters } from "./FiltersList";
import { Checkbox } from "@/components/ui/checkbox";
export default function FilterSheet({
filters,
creators,
onCategoryChange,
onCreatorChange,
categories,
}: {
filters: Filters;
creators: string[];
onCategoryChange: (category: CategoryKey) => void;
onCreatorChange: (creator: string) => void;
categories: Array<{ key: CategoryKey; name: string }>;
}) {
const [isOpen, setIsOpen] = useState(false);
const [isSheetVisible, setIsSheetVisible] = useState(false);
// Animation
useEffect(() => {
if (isOpen) {
setIsSheetVisible(true);
} else {
const timer = setTimeout(() => {
setIsSheetVisible(false);
}, 300);
return () => clearTimeout(timer);
}
}, [isOpen]);
return (
<div className="m-0 inline w-fit p-0">
<Button
onClick={() => {
setIsSheetVisible(true);
requestAnimationFrame(() => {
requestAnimationFrame(() => {
setIsOpen(true);
});
});
}}
variant={"link"}
className="m-0 p-0 hover:no-underline"
>
<FilterChip name="All filters" />
</Button>
{isSheetVisible && (
<>
<div
className={cn(
"absolute bottom-2 left-2 top-2 z-20 w-3/4 max-w-[22.5rem] space-y-4 rounded-[0.75rem] bg-white py-4 shadow-[0_4px_12px_2px_rgba(0,0,0,0.1)] transition-all",
isOpen
? "translate-x-0 duration-300 ease-out"
: "-translate-x-full duration-300 ease-out",
)}
>
{/* Top */}
<div className="flex items-center justify-between px-5">
<p className="font-sans text-base text-[#040404]">Filters</p>
<Button
variant="ghost"
size="icon"
onClick={() => setIsOpen(false)}
>
<X className="h-5 w-5" />
</Button>
</div>
<Separator className="h-[1px] w-full text-zinc-300" />
{/* Categories */}
<div className="px-5">
<p className="font-sans text-base font-medium text-zinc-800">
Categories
</p>
<div className="mt-2 space-y-2">
{categories.map((category) => (
<div
key={category.key}
className="flex items-center space-x-2"
>
<Checkbox
id={category.key}
checked={filters.categories[category.key]}
onCheckedChange={() => onCategoryChange(category.key)}
className="border border-[#D4D4D4] shadow-none data-[state=checked]:bg-white data-[state=checked]:text-zinc-500"
/>
<label
htmlFor={category.key}
className="font-sans text-sm leading-[1.375rem] text-zinc-600"
>
{category.name}
</label>
</div>
))}
</div>
</div>
<Separator className="h-[1px] w-full text-zinc-300" />
{/* Created By */}
<div className="px-5">
<p className="font-sans text-base font-medium text-zinc-800">
Created by
</p>
<div className="mt-2 space-y-2">
{creators.map((creator) => (
<div key={creator} className="flex items-center space-x-2">
<Checkbox
id={`creator-${creator}`}
checked={filters.createdBy.includes(creator)}
onCheckedChange={() => onCreatorChange(creator)}
className="border border-[#D4D4D4] shadow-none data-[state=checked]:bg-white data-[state=checked]:text-zinc-500"
/>
<label
htmlFor={`creator-${creator}`}
className="font-sans text-sm leading-[1.375rem] text-zinc-600"
>
{creator}
</label>
</div>
))}
</div>
</div>
</div>
</>
)}
</div>
);
}

View File

@@ -1,16 +1,102 @@
import { useState, useEffect } from "react";
import FilterChip from "../FilterChip";
import FilterSheet from "./FilterSheet";
export type CategoryKey =
| "blocks"
| "integrations"
| "marketplace_agents"
| "my_agents"
| "templates";
export interface Filters {
categories: {
blocks: boolean;
integrations: boolean;
marketplace_agents: boolean;
my_agents: boolean;
templates: boolean;
};
createdBy: string[];
}
const FiltersList = () => {
return (
<div className="space-x-3">
<FilterChip name="All filters" />
{/* Created by filters */}
const [filters, setFilters] = useState<Filters>({
categories: {
blocks: false,
integrations: false,
marketplace_agents: false,
my_agents: false,
templates: false,
},
createdBy: [],
});
{/* Fixed filters */}
<FilterChip name="Blocks" />
<FilterChip name="Integrations" />
<FilterChip name="Marketplace agents" />
<FilterChip name="My agents" />
const [creators, setCreators] = useState<string[]>([]);
const categories: Array<{ key: CategoryKey; name: string }> = [
{ key: "blocks", name: "Blocks" },
{ key: "integrations", name: "Integrations" },
{ key: "marketplace_agents", name: "Marketplace agents" },
{ key: "my_agents", name: "My agents" },
{ key: "templates", name: "Templates" },
];
useEffect(() => {
const mockCreators = ["Abhi", "Abhi 1", "Abhi 2", "Abhi 3", "Abhi 4"];
setCreators(mockCreators);
}, []);
const handleCategoryFilter = (category: CategoryKey) => {
setFilters({
...filters,
categories: {
...filters.categories,
[category]: !filters.categories[category],
},
});
};
const handleCreatorFilter = (creator: string) => {
const updatedCreators = filters.createdBy.includes(creator)
? filters.createdBy.filter((c) => c !== creator)
: [...filters.createdBy, creator];
setFilters({
...filters,
createdBy: updatedCreators,
});
};
return (
<div className="flex flex-wrap gap-3">
<FilterSheet
filters={filters}
creators={creators}
categories={categories}
onCategoryChange={handleCategoryFilter}
onCreatorChange={handleCreatorFilter}
/>
{filters.createdBy.map((creator) => (
<FilterChip
key={creator}
name={creator}
selected={true}
number={4}
onClick={() => handleCreatorFilter(creator)}
/>
))}
{categories.map((category) => (
<FilterChip
key={category.key}
name={category.name}
selected={filters.categories[category.key]}
onClick={() => handleCategoryFilter(category.key)}
/>
))}
</div>
);
};