Avatar
A comprehensive filtering system with multiple filter types, operators, and visual indicators for data organization.
'use client'
import { Button } from '@nerdfish/react/button'
import {
createFilter,
Filters,
type Filter,
type FilterFieldConfig,
} from '@nerdfish/react/filters'
import { cn } from '@nerdfish/utils/class'
import {
AlertCircleIcon,
BanIcon,
BellIcon,
BuildingIcon,
CalendarIcon,
CheckCircleIcon,
ClockIcon,
FunnelXIcon,
GlobeIcon,
MailIcon,
PhoneIcon,
SlidersHorizontalIcon,
StarIcon,
TimerIcon,
TrendingUpIcon,
TypeIcon,
UserRoundCheckIcon,
UsersIcon,
WalletIcon,
} from 'lucide-react'
import { useState } from 'react'
// Priority icon component
const PriorityIconIcon = ({ priority }: { priority: string }) => {
const colors = {
low: 'bg-green-500',
medium: 'bg-yellow-500',
high: 'bg-violet-500',
urgent: 'bg-orange-500',
critical: 'bg-red-500',
}
return (
<div
className={cn(
'size-2.25 shrink-0 rounded-full',
colors[priority as keyof typeof colors],
)}
/>
)
}
export default function FiltersExample() {
// Example: All Possible Filter Field Types with Grouping
const fields: FilterFieldConfig[] = [
{
group: 'Basic',
fields: [
{
key: 'text',
label: 'Text',
type: 'text',
icon: <TypeIcon />,
placeholder: 'Search text...',
},
{
key: 'email',
label: 'Email',
type: 'email',
icon: <MailIcon />,
placeholder: 'user@example.com',
},
{
key: 'website',
label: 'Website',
icon: <GlobeIcon />,
type: 'url',
className: 'w-40',
placeholder: 'https://example.com',
},
{
key: 'phone',
label: 'Phone',
icon: <PhoneIcon />,
type: 'tel',
className: 'w-40',
placeholder: '+1 (123) 456-7890',
},
{
key: 'isActive',
label: 'Is active ?',
icon: <CheckCircleIcon />,
type: 'boolean',
},
],
},
{
group: 'Select',
fields: [
{
key: 'status',
label: 'Status',
icon: <BellIcon />,
type: 'select',
searchable: false,
className: 'w-[200px]',
options: [
{
value: 'todo',
label: 'To Do',
icon: <ClockIcon className="text-primary size-3" />,
},
{
value: 'in-progress',
label: 'In Progress',
icon: <AlertCircleIcon className="size-3 text-yellow-500" />,
},
{
value: 'done',
label: 'Done',
icon: <CheckCircleIcon className="size-3 text-green-500" />,
},
{
value: 'cancelled',
label: 'Cancelled',
icon: <BanIcon className="text-destructive size-3" />,
},
],
},
{
key: 'priority',
label: 'Priority',
icon: <SlidersHorizontalIcon />,
type: 'multiselect',
className: 'w-[180px]',
selectedOptionsClassName: '-space-x-1',
options: [
{
value: 'low',
label: 'Low',
icon: <PriorityIconIcon priority="low" />,
},
{
value: 'medium',
label: 'Medium',
icon: <PriorityIconIcon priority="medium" />,
},
{
value: 'high',
label: 'High',
icon: <PriorityIconIcon priority="high" />,
},
{
value: 'urgent',
label: 'Urgent',
icon: <PriorityIconIcon priority="urgent" />,
},
{
value: 'critical',
label: 'Critical',
icon: <PriorityIconIcon priority="critical" />,
},
],
},
{
key: 'assignee',
label: 'Assignee',
icon: <UserRoundCheckIcon />,
type: 'multiselect',
maxSelections: 5,
options: [
{
value: 'john',
label: 'John Doe',
icon: <div className="size-3 rounded-full bg-blue-400" />,
},
{
value: 'jane',
label: 'Jane Smith',
icon: <div className="size-3 rounded-full bg-green-400" />,
},
{
value: 'bob',
label: 'Bob Johnson',
icon: <div className="size-3 rounded-full bg-purple-400" />,
},
{
value: 'unassigned',
label: 'Unassigned',
icon: <BuildingIcon className="size-3 text-gray-400" />,
},
],
},
{
key: 'userType',
label: 'User Type',
icon: <UsersIcon />,
type: 'select',
searchable: false,
className: 'w-[200px]',
options: [
{
value: 'premium',
label: 'Premium',
icon: <StarIcon className="size-3 text-yellow-500" />,
},
{
value: 'standard',
label: 'Standard',
icon: <BuildingIcon className="size-3 text-blue-500" />,
},
{
value: 'trial',
label: 'Trial',
icon: <ClockIcon className="size-3 text-gray-500" />,
},
],
},
],
},
{
group: 'Date & Time',
fields: [
{
key: 'dueDate',
label: 'Due Date',
icon: <CalendarIcon />,
type: 'date',
className: 'w-36',
},
{
key: 'orderDate',
label: 'Order Date',
icon: <CalendarIcon />,
type: 'select',
searchable: false,
className: 'w-[200px]',
options: [
{ value: 'past', label: 'in the past' },
{ value: '24h', label: '24 hours from now' },
{ value: '3d', label: '3 days from now' },
{ value: '1w', label: '1 week from now' },
{ value: '1m', label: '1 month from now' },
{ value: '3m', label: '3 months from now' },
],
},
{
key: 'dateRange',
label: 'Date Range',
icon: <CalendarIcon />,
type: 'daterange',
},
{
key: 'createdAt',
label: 'Created At',
icon: <ClockIcon />,
type: 'datetime',
},
{
key: 'workingHours',
label: 'Working Hours',
icon: <TimerIcon />,
type: 'time',
},
],
},
{
group: 'Numbers',
fields: [
{
key: 'score',
label: 'Score',
icon: <StarIcon />,
type: 'number',
min: 0,
max: 100,
step: 1,
},
{
key: 'salary',
label: 'Salary',
icon: <WalletIcon />,
type: 'number',
prefix: '$',
className: 'w-24',
min: 0,
max: 500000,
step: 1000,
},
{
key: 'completion',
label: 'Completion',
icon: <TrendingUpIcon />,
className: 'w-24',
suffix: '%',
type: 'number',
step: 5,
},
],
},
]
const [filters, setFilters] = useState<Filter[]>([
createFilter('priority', 'contains', ['low', 'medium', 'critical']),
])
return (
<div className="content-starIcont items-starIcont self-starIcont flex grow gap-2.5 space-y-6">
<div className="flex-1">
<Filters
filters={filters}
fields={fields}
variant="outline"
onChange={setFilters}
/>
</div>
{filters.length > 0 ? (
<div>
<Button variant="outline" onClick={() => setFilters([])}>
<FunnelXIcon className="size-4" /> Clear
</Button>
</div>
) : null}
</div>
)
}
Debug
'use client'
import { Avatar, AvatarFallback, AvatarImage } from '@nerdfish/react/avatar'
import { Button } from '@nerdfish/react/button'
import {
createFilter,
Filters,
type Filter,
type FilterFieldConfig,
} from '@nerdfish/react/filters'
import {
AlertCircle,
Calendar,
CheckCircle,
FunnelX,
Globe,
Mail,
Star,
Tag,
User,
} from 'lucide-react'
import { useCallback, useState } from 'react'
// Priority icon component
const PriorityIcon = ({ priority }: { priority: string }) => {
const colors = {
low: 'text-green-500',
medium: 'text-yellow-500',
high: 'text-orange-500',
urgent: 'text-red-500',
}
return <Star className={colors[priority as keyof typeof colors]} />
}
// Basic filter fields for outline variant demo
const fields: FilterFieldConfig[] = [
{
key: 'text',
label: 'Text',
icon: <Tag className="size-3.5" />,
type: 'text',
className: 'w-36',
placeholder: 'Search text...',
},
{
key: 'email',
label: 'Email',
icon: <Mail className="size-3.5" />,
type: 'email',
className: 'w-48',
placeholder: 'user@example.com',
},
{
key: 'website',
label: 'Website',
icon: <Globe className="size-3.5" />,
type: 'url',
className: 'w-40',
placeholder: 'https://example.com',
},
{
key: 'assignee',
label: 'Assignee',
icon: <User className="size-3.5" />,
type: 'multiselect',
className: 'w-[200px]',
options: [
{
value: 'john',
label: 'John Doe',
icon: (
<Avatar className="size-5">
<AvatarImage
src="https://randomuser.me/api/portraits/men/1.jpg"
alt="John Doe"
/>
<AvatarFallback>JD</AvatarFallback>
</Avatar>
),
},
{
value: 'jane',
label: 'Jane Smith',
icon: (
<Avatar className="size-5">
<AvatarImage
src="https://randomuser.me/api/portraits/women/2.jpg"
alt="Jane Smith"
/>
<AvatarFallback>JS</AvatarFallback>
</Avatar>
),
},
{
value: 'bob',
label: 'Bob Johnson',
icon: (
<Avatar className="size-5">
<AvatarImage
src="https://randomuser.me/api/portraits/men/3.jpg"
alt="Bob Johnson"
/>
<AvatarFallback>BJ</AvatarFallback>
</Avatar>
),
},
{
value: 'alice',
label: 'Alice Brown',
icon: (
<Avatar className="size-5">
<AvatarImage
src="https://randomuser.me/api/portraits/women/4.jpg"
alt="Alice Brown"
/>
<AvatarFallback>AB</AvatarFallback>
</Avatar>
),
},
{
value: 'nick',
label: 'Nick Bold',
icon: (
<Avatar className="size-5">
<AvatarImage
src="https://randomuser.me/api/portraits/men/4.jpg"
alt="Nick Bold"
/>
<AvatarFallback>NB</AvatarFallback>
</Avatar>
),
},
],
},
{
key: 'priority',
label: 'Priority',
icon: <AlertCircle className="size-3.5" />,
type: 'multiselect',
className: 'w-[180px]',
options: [
{ value: 'low', label: 'Low', icon: <PriorityIcon priority="low" /> },
{
value: 'medium',
label: 'Medium',
icon: <PriorityIcon priority="medium" />,
},
{
value: 'high',
label: 'High',
icon: <PriorityIcon priority="high" />,
},
{
value: 'urgent',
label: 'Urgent',
icon: <PriorityIcon priority="urgent" />,
},
],
},
{
key: 'dueDate',
label: 'Due Date',
icon: <Calendar className="size-3.5" />,
type: 'date',
className: 'w-36',
},
{
key: 'score',
label: 'Score',
icon: <Star className="size-3.5" />,
type: 'number',
min: 0,
max: 100,
step: 1,
},
{
key: 'isActive',
label: 'Active Status',
icon: <CheckCircle className="size-3.5" />,
type: 'boolean',
},
]
export default function FiltersDebugExample() {
const [filters, setFilters] = useState<Filter[]>([
createFilter('assignee', 'is', ['john']),
createFilter('isActive', 'is', [true]),
])
const handleFiltersChange = useCallback((updatedFilters: Filter[]) => {
console.info('Filters updated:', updatedFilters)
setFilters(updatedFilters)
}, [])
return (
<div className="flex grow content-start items-start gap-2.5 self-start">
<div className="grow space-y-5">
{/* Filters Section */}
<div className="flex items-start gap-2.5">
<div className="flex-1">
<Filters
filters={filters}
fields={fields}
variant="outline"
onChange={handleFiltersChange}
/>
</div>
{filters.length > 0 ? (
<Button variant="outline" onClick={() => setFilters([])}>
<FunnelX className="size-4" /> Clear
</Button>
) : null}
</div>
{/* Debug Block */}
<pre className="bg-muted rounded-base p-friends mt-2 max-h-100 overflow-auto overflow-x-auto border text-xs">
{JSON.stringify(filters, null, 2)}
</pre>
</div>
</div>
)
}