filters-default

PreviousNext
Docs
reuicomponent

Preview

Loading preview…
registry/default/components/filters/default.tsx
'use client';

import { useCallback, useState } from 'react';
import { cn } from '@/registry/default/lib/utils';
import { Button } from '@/registry/default/ui/button';
import { createFilter, Filters, type Filter, type FilterFieldConfig } from '@/registry/default/ui/filters';
import {
  AlertCircle,
  Ban,
  Bell,
  Building,
  Calendar,
  CheckCircle,
  Clock,
  FunnelX,
  Globe,
  Mail,
  Phone,
  SlidersHorizontal,
  Star,
  Timer,
  TrendingUp,
  Type,
  UserRoundCheck,
  Users,
  Wallet,
} from 'lucide-react';

// Priority icon component
const PriorityIcon = ({ 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 FiltersDemo() {
  // Example: All Possible Filter Field Types with Grouping
  const fields: FilterFieldConfig[] = [
    {
      group: 'Basic',
      fields: [
        {
          key: 'text',
          label: 'Text',
          type: 'text',
          icon: <Type />,
          placeholder: 'Search text...',
        },
        {
          key: 'email',
          label: 'Email',
          type: 'email',
          icon: <Mail />,
          placeholder: 'user@example.com',
        },
        {
          key: 'website',
          label: 'Website',
          icon: <Globe />,
          type: 'url',
          className: 'w-40',
          placeholder: 'https://example.com',
        },
        {
          key: 'phone',
          label: 'Phone',
          icon: <Phone />,
          type: 'tel',
          className: 'w-40',
          placeholder: '+1 (123) 456-7890',
        },
        {
          key: 'isActive',
          label: 'Is active ?',
          icon: <CheckCircle />,
          type: 'boolean',
        },
      ],
    },
    {
      group: 'Select',
      fields: [
        {
          key: 'status',
          label: 'Status',
          icon: <Bell />,
          type: 'select',
          searchable: false,
          className: 'w-[200px]',
          options: [
            { value: 'todo', label: 'To Do', icon: <Clock className="size-3 text-primary" /> },
            { value: 'in-progress', label: 'In Progress', icon: <AlertCircle className="size-3 text-yellow-500" /> },
            { value: 'done', label: 'Done', icon: <CheckCircle className="size-3 text-green-500" /> },
            { value: 'cancelled', label: 'Cancelled', icon: <Ban className="size-3 text-destructive" /> },
          ],
        },
        {
          key: 'priority',
          label: 'Priority',
          icon: <SlidersHorizontal />,
          type: 'multiselect',
          className: 'w-[180px]',
          selectedOptionsClassName: '-space-x-1',
          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" /> },
            { value: 'critical', label: 'Critical', icon: <PriorityIcon priority="critical" /> },
          ],
        },
        {
          key: 'assignee',
          label: 'Assignee',
          icon: <UserRoundCheck />,
          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: <Building className="size-3 text-gray-400" /> },
          ],
        },
        {
          key: 'userType',
          label: 'User Type',
          icon: <Users />,
          type: 'select',
          searchable: false,
          className: 'w-[200px]',
          options: [
            { value: 'premium', label: 'Premium', icon: <Star className="size-3 text-yellow-500" /> },
            { value: 'standard', label: 'Standard', icon: <Building className="size-3 text-blue-500" /> },
            { value: 'trial', label: 'Trial', icon: <Clock className="size-3 text-gray-500" /> },
          ],
        },
      ],
    },
    {
      group: 'Date & Time',
      fields: [
        {
          key: 'dueDate',
          label: 'Due Date',
          icon: <Calendar />,
          type: 'date',
          className: 'w-36',
        },
        {
          key: 'orderDate',
          label: 'Order Date',
          icon: <Calendar />,
          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: <Calendar />,
          type: 'daterange',
        },
        {
          key: 'createdAt',
          label: 'Created At',
          icon: <Clock />,
          type: 'datetime',
        },
        {
          key: 'workingHours',
          label: 'Working Hours',
          icon: <Timer />,
          type: 'time',
        },
      ],
    },
    {
      group: 'Numbers',
      fields: [
        {
          key: 'score',
          label: 'Score',
          icon: <Star />,
          type: 'number',
          min: 0,
          max: 100,
          step: 1,
        },
        {
          key: 'salary',
          label: 'Salary',
          icon: <Wallet />,
          type: 'number',
          prefix: '$',
          className: 'w-24',
          min: 0,
          max: 500000,
          step: 1000,
        },
        {
          key: 'completion',
          label: 'Completion',
          icon: <TrendingUp />,
          className: 'w-24',
          suffix: '%',
          type: 'number',
          step: 5,
        },
      ],
    },
  ];

  const [filters, setFilters] = useState<Filter[]>([
    createFilter('priority', 'contains', ['low', 'medium', 'critical']),
  ]);

  const handleFiltersChange = useCallback((filters: Filter[]) => {
    console.log('Filters updated:', filters);
    setFilters(filters);
  }, []);

  return (
    <div className="flex items-start gap-2.5 grow space-y-6 self-start content-start">
      <div className="flex-1">
        <Filters filters={filters} fields={fields} variant="outline" onChange={handleFiltersChange} />
      </div>

      {filters.length > 0 && (
        <Button variant="outline" onClick={() => setFilters([])}>
          <FunnelX /> Clear
        </Button>
      )}
    </div>
  );
}

Installation

npx shadcn@latest add @reui/filters-default

Usage

import { FiltersDefault } from "@/components/filters-default"
<FiltersDefault />