import type { Column, Table } from '@tanstack/react-table'; import { SearchIcon, X, XIcon } from 'lucide-react'; import { useCallback, useMemo, useRef, useState } from 'react'; import { Button } from '@/components/ui/button'; import { DataTableDateFilter } from '@/components/ui/data-table/data-table-date-filter'; import { DataTableFacetedFilter } from '@/components/ui/data-table/data-table-faceted-filter'; import { DataTableSliderFilter } from '@/components/ui/data-table/data-table-slider-filter'; import { DataTableViewOptions } from '@/components/ui/data-table/data-table-view-options'; import { Input } from '@/components/ui/input'; import { useSearchQueryState } from '@/hooks/use-search-query-state'; import { cn } from '@/lib/utils'; interface DataTableToolbarProps extends React.ComponentProps<'div'> { table: Table; globalSearchKey?: string; globalSearchPlaceholder?: string; } export function DataTableToolbarContainer({ className, ...props }: React.ComponentProps<'div'>) { return (
); } export function DataTableToolbar({ table, children, className, globalSearchKey, globalSearchPlaceholder, ...props }: DataTableToolbarProps) { const { search, setSearch } = useSearchQueryState({ searchKey: globalSearchKey, }); const isFiltered = table.getState().columnFilters.length > 0; const columns = useMemo( () => table.getAllColumns().filter((column) => column.getCanFilter()), [table] ); const onReset = useCallback(() => { table.resetColumnFilters(); }, [table]); return (
{globalSearchKey && ( )} {columns.map((column) => ( ))} {isFiltered && ( )}
{children}
); } interface DataTableToolbarFilterProps { column: Column; } function DataTableToolbarFilter({ column, }: DataTableToolbarFilterProps) { { const columnMeta = column.columnDef.meta; const getTitle = useCallback(() => { return columnMeta?.label ?? columnMeta?.placeholder ?? column.id; }, [columnMeta, column]); const onFilterRender = useCallback(() => { if (!columnMeta?.variant) { return null; } switch (columnMeta.variant) { case 'text': return ( column.setFilterValue(value)} placeholder={columnMeta.placeholder ?? columnMeta.label} value={(column.getFilterValue() as string) ?? ''} /> ); case 'number': return (
column.setFilterValue(event.target.value)} placeholder={getTitle()} type="number" value={(column.getFilterValue() as string) ?? ''} /> {columnMeta.unit && ( {columnMeta.unit} )}
); case 'range': return ; case 'date': case 'dateRange': return ( ); case 'select': case 'multiSelect': return ( ); default: return null; } }, [column, columnMeta]); return onFilterRender(); } } interface AnimatedSearchInputProps { placeholder?: string; value: string; onChange: (value: string) => void; } export function AnimatedSearchInput({ placeholder, value, onChange, }: AnimatedSearchInputProps) { const [isFocused, setIsFocused] = useState(false); const inputRef = useRef(null); const isExpanded = isFocused || (value?.length ?? 0) > 0; const handleClear = useCallback(() => { onChange(''); // Re-focus after clearing requestAnimationFrame(() => inputRef.current?.focus()); }, [onChange]); return (
setIsFocused(false)} onChange={(e) => onChange(e.target.value)} onFocus={() => setIsFocused(true)} placeholder={placeholder} ref={inputRef} size="sm" value={value} /> {isExpanded && value && ( )}
); }