fix filter sheets

This commit is contained in:
Abhimanyu Yadav
2025-05-20 11:25:46 +05:30
parent 1971a62684
commit e718d3d3d8
4 changed files with 97 additions and 23 deletions

View File

@@ -1,11 +1,12 @@
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { X } from "lucide-react";
import React, { ButtonHTMLAttributes } from "react";
import React, { ButtonHTMLAttributes, useState } from "react";
interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
selected?: boolean;
number?: number;
needHover?: boolean;
name?: string;
}
@@ -13,27 +14,31 @@ const FilterChip: React.FC<Props> = ({
selected = false,
number,
name,
needHover,
className,
...rest
}) => {
const [isHovering, setIsHovering] = useState(false);
return (
<Button
className={cn(
"group w-fit space-x-1 rounded-[1.5rem] border border-zinc-300 bg-transparent px-[0.625rem] py-[0.375rem] shadow-none hover:bg-zinc-100 focus:ring-0 disabled:pointer-events-none",
)}
{...rest}
onMouseEnter={() => setIsHovering(true)}
onMouseLeave={() => setIsHovering(false)}
>
<span className="font-sans text-sm font-medium leading-[1.375rem] text-zinc-600 group-disabled:text-zinc-400">
{name}
</span>
{selected &&
(number ? (
(needHover && isHovering && number ? (
<span className="flex h-[1.375rem] items-center rounded-[1.25rem] bg-violet-700 p-[0.375rem] text-zinc-50">
{number}
{number > 100 ? "100+" : number}
</span>
) : (
<span className="flex h-5 w-5 items-center justify-center rounded-full bg-zinc-600">
<X className="h-4 w-4 rounded-full text-white" strokeWidth={1.25} />
<span className="flex h-4 w-4 items-center justify-center rounded-full bg-zinc-600">
<X className="h-3 w-3 rounded-full text-white" strokeWidth={2} />
</span>
))}
</Button>

View File

@@ -1,7 +1,7 @@
// Used v0 for this component, so review it very carefully
import FilterChip from "../FilterChip";
import { useState, useEffect } from "react";
import { useState, useEffect, Dispatch, SetStateAction } from "react";
import { Button } from "@/components/ui/button";
import { X } from "lucide-react";
import { cn } from "@/lib/utils";
@@ -12,30 +12,72 @@ import { Checkbox } from "@/components/ui/checkbox";
export default function FilterSheet({
filters,
creators,
onCategoryChange,
onCreatorChange,
setFilters,
categories,
}: {
filters: Filters;
creators: string[];
onCategoryChange: (category: CategoryKey) => void;
onCreatorChange: (creator: string) => void;
setFilters: Dispatch<SetStateAction<Filters>>;
categories: Array<{ key: CategoryKey; name: string }>;
}) {
const [isOpen, setIsOpen] = useState(false);
const [isSheetVisible, setIsSheetVisible] = useState(false);
const [localFilters, setLocalFilters] = useState<Filters>(filters);
// Animation
useEffect(() => {
if (isOpen) {
setIsSheetVisible(true);
// Reset local filters to current filters when opening
setLocalFilters(filters);
} else {
const timer = setTimeout(() => {
setIsSheetVisible(false);
}, 300);
return () => clearTimeout(timer);
}
}, [isOpen]);
}, [isOpen, filters]);
const onCategoryChange = (category: CategoryKey) => {
setLocalFilters({
...localFilters,
categories: {
...localFilters.categories,
[category]: !localFilters.categories[category],
},
});
};
const onCreatorChange = (creator: string) => {
const updatedCreators = localFilters.createdBy.includes(creator)
? localFilters.createdBy.filter((c) => c !== creator)
: [...localFilters.createdBy, creator];
setLocalFilters({
...localFilters,
createdBy: updatedCreators,
});
};
const handleApplyFilters = () => {
setFilters(localFilters);
setIsOpen(false);
};
const handleClearFilters = () => {
const clearedFilters: Filters = {
categories: {
blocks: false,
integrations: false,
marketplace_agents: false,
my_agents: false,
templates: false,
},
createdBy: [],
};
setFilters(clearedFilters);
setIsOpen(false);
};
return (
<div className="m-0 inline w-fit p-0">
@@ -80,11 +122,11 @@ export default function FilterSheet({
{/* Categories */}
<div className="px-5">
<div className="space-y-4 px-5">
<p className="font-sans text-base font-medium text-zinc-800">
Categories
</p>
<div className="mt-2 space-y-2">
<div className="space-y-2">
{categories.map((category) => (
<div
key={category.key}
@@ -92,7 +134,7 @@ export default function FilterSheet({
>
<Checkbox
id={category.key}
checked={filters.categories[category.key]}
checked={localFilters.categories[category.key]}
onCheckedChange={() => onCategoryChange(category.key)}
className="border border-[#D4D4D4] shadow-none data-[state=checked]:border-none data-[state=checked]:bg-zinc-500 data-[state=checked]:text-white"
/>
@@ -111,18 +153,18 @@ export default function FilterSheet({
{/* Created By */}
<div className="px-5">
<div className="space-y-4 px-5">
<p className="font-sans text-base font-medium text-zinc-800">
Created by
</p>
<div className="mt-2 space-y-2">
<div className="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)}
checked={localFilters.createdBy.includes(creator)}
onCheckedChange={() => onCreatorChange(creator)}
className="border border-[#D4D4D4] shadow-none data-[state=checked]:bg-white data-[state=checked]:text-zinc-500"
className="border border-[#D4D4D4] shadow-none data-[state=checked]:border-none data-[state=checked]:bg-zinc-500 data-[state=checked]:text-white"
/>
<label
htmlFor={`creator-${creator}`}
@@ -133,6 +175,30 @@ export default function FilterSheet({
</div>
))}
</div>
<Button
variant={"link"}
className="m-0 p-0 font-sans text-sm font-medium leading-[1.375rem] text-zinc-800 underline hover:text-zinc-600"
>
More
</Button>
</div>
{/* Footer buttons */}
<div className="absolute bottom-0 flex w-full justify-between gap-3 border-t border-zinc-300 px-5 py-3">
<Button
className="min-w-[5rem] rounded-[0.5rem] border-none px-1.5 py-2 font-sans text-sm font-medium leading-[1.375rem] text-zinc-800 shadow-none ring-1 ring-zinc-400"
variant={"outline"}
onClick={handleClearFilters}
>
Clear
</Button>
<Button
className="min-w-[6.25rem] rounded-[0.5rem] border-none bg-zinc-700 px-1.5 py-2 font-sans text-sm font-medium leading-[1.375rem] text-white shadow-none ring-1 ring-zinc-700"
onClick={handleApplyFilters}
>
Apply filters
</Button>
</div>
</div>
</>

View File

@@ -76,16 +76,14 @@ const FiltersList = () => {
filters={filters}
creators={creators}
categories={categories}
onCategoryChange={handleCategoryFilter}
onCreatorChange={handleCreatorFilter}
setFilters={setFilters}
/>
{filters.createdBy.map((creator) => (
<FilterChip
key={creator}
name={creator}
name={"Created by " + creator}
selected={true}
number={4}
onClick={() => handleCreatorFilter(creator)}
/>
))}
@@ -94,6 +92,11 @@ const FiltersList = () => {
<FilterChip
key={category.key}
name={category.name}
needHover={
Object.values(filters.categories).filter(Boolean).length === 1 &&
filters.categories[category.key]
}
number={103}
selected={filters.categories[category.key]}
onClick={() => handleCategoryFilter(category.key)}
/>

View File

@@ -14,7 +14,7 @@ const Checkbox = React.forwardRef<
<CheckboxPrimitive.Root
ref={ref}
className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-neutral-200 border-neutral-900 shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-neutral-950 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-neutral-900 data-[state=checked]:text-neutral-50 dark:border-neutral-50 dark:border-neutral-800 dark:focus-visible:ring-neutral-300 dark:data-[state=checked]:bg-neutral-50 dark:data-[state=checked]:text-neutral-900",
"peer h-4 w-4 shrink-0 rounded-sm border border-neutral-900 shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-neutral-950 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-neutral-900 data-[state=checked]:text-neutral-50 dark:border-neutral-50 dark:border-neutral-800 dark:focus-visible:ring-neutral-300 dark:data-[state=checked]:bg-neutral-50 dark:data-[state=checked]:text-neutral-900",
className,
)}
{...props}