Componentdata0.97 KB gzipZero dependencies
Filters Drawer
Collapsible filter panel with counts.
Preview
Live Preview
Installation
Tokens guide →Add to your project via CLI (zero dependencies, code you own):
npx bigbullui add filters-drawerOr install the full npm package:
npm install bigbulluiUsage
Open in StackBlitzimport { FiltersDrawer } from "@/components/ui/filters-drawer";
<FiltersDrawer />Usage guidance
Use Filters Drawer when a ticket stub surface needs Collapsible filter panel with counts. Keep props minimal and prefer composition with sibling stubs.
Avoid Filters Drawer for long form flows or dense data grids where a dedicated layout block fits better.
Source code
filters-drawer.tsxZero dependencies (only React + utils). Copy and paste directly into your project:
"use client";
import * as React from "react";
import { cn } from "./lib/utils";
export interface FiltersDrawerProps extends React.HTMLAttributes<HTMLDivElement> {
onApply?: (filters: string[]) => void;
}
/** Filters drawer stub: inline collapsible filter panel with counts. */
export function FiltersDrawer({ onApply, className, ...props }: FiltersDrawerProps) {
const [open, setOpen] = React.useState(false);
const [selected, setSelected] = React.useState<string[]>(["VIP"]);
const options = ["VIP", "Orchestra", "Balcony", "Standby"];
const toggle = (o: string) => setSelected((s) => (s.includes(o) ? s.filter((x) => x !== o) : [...s, o]));
return (
<div className={cn("w-full max-w-sm rounded-lg border border-border bg-card", className)} {...props}>
<button
type="button"
aria-expanded={open}
onClick={() => setOpen((v) => !v)}
className="flex w-full items-center justify-between p-3 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
>
<span className="font-mono text-[10px] uppercase tracking-[0.15em] text-muted-foreground">Filters{selected.length > 0 && ` · ${selected.length}`}</span>
<span className="font-mono text-xs text-accent" aria-hidden="true">{open ? "−" : "+"}</span>
</button>
{open && (
<div className="space-y-1.5 border-t border-dashed border-border p-3">
{options.map((o) => (
<label key={o} className="flex cursor-pointer items-center gap-2 rounded-md border border-dashed border-border px-2.5 py-1.5 font-mono text-xs text-foreground has-checked:border-accent">
<input type="checkbox" checked={selected.includes(o)} onChange={() => toggle(o)} className="size-3.5 accent-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" />
{o}
</label>
))}
<button
type="button"
onClick={() => onApply?.(selected)}
className="mt-1 w-full rounded-md bg-primary px-3 py-1.5 font-mono text-[11px] font-bold uppercase tracking-wider text-primary-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
>
Apply {selected.length} filters
</button>
</div>
)}
</div>
);
}