feat(mobile): add FilterChips primitive (horizontal chip row with Add-filter trigger)
This commit is contained in:
67
src/components/shared/filter-chips.tsx
Normal file
67
src/components/shared/filter-chips.tsx
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { X, Filter } from 'lucide-react';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
export type ActiveFilter = {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
/** Compact value rendered on the chip after the label (e.g. ": Active"). */
|
||||||
|
value?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Horizontal chip row for active filters, with an "Add filter" trigger that
|
||||||
|
* the caller wires to a `<Drawer>`. Active chips are dismissable via the X
|
||||||
|
* button. Scrolls horizontally on mobile when there are many filters.
|
||||||
|
*/
|
||||||
|
export function FilterChips({
|
||||||
|
active,
|
||||||
|
onRemove,
|
||||||
|
onAddClick,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
active: ActiveFilter[];
|
||||||
|
onRemove: (id: string) => void;
|
||||||
|
onAddClick: () => void;
|
||||||
|
className?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'flex items-center gap-2 overflow-x-auto -mx-3 px-3 sm:mx-0 sm:px-0 sm:flex-wrap sm:overflow-visible',
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={onAddClick}
|
||||||
|
className="shrink-0 inline-flex items-center gap-1.5 rounded-full border border-input bg-background px-3 h-9 text-sm font-medium text-foreground hover:bg-accent"
|
||||||
|
>
|
||||||
|
<Filter className="size-4" aria-hidden />
|
||||||
|
Add filter
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{active.map((filter) => (
|
||||||
|
<span
|
||||||
|
key={filter.id}
|
||||||
|
className="shrink-0 inline-flex items-center gap-1 rounded-full bg-secondary text-secondary-foreground px-3 h-9 text-sm font-medium"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{filter.label}
|
||||||
|
{filter.value ? `: ${filter.value}` : ''}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => onRemove(filter.id)}
|
||||||
|
aria-label={`Remove ${filter.label} filter`}
|
||||||
|
className="-mr-1 inline-flex size-6 items-center justify-center rounded-full hover:bg-secondary-foreground/10"
|
||||||
|
>
|
||||||
|
<X className="size-3.5" aria-hidden />
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user