Files
igny8/frontend/src/components/common/ColumnSelector.tsx
2025-11-12 23:29:31 +05:00

132 lines
4.4 KiB
TypeScript

/**
* ColumnSelector Component
* Dropdown with checkboxes to show/hide table columns
*/
import React, { useState, useRef, useEffect } from 'react';
import { ChevronDownIcon } from '../../icons';
import Checkbox from '../form/input/Checkbox';
interface ColumnSelectorProps {
columns: Array<{ key: string; label: string; defaultVisible?: boolean }>;
visibleColumns: Set<string>;
onToggleColumn: (columnKey: string) => void;
className?: string;
}
export default function ColumnSelector({
columns,
visibleColumns,
onToggleColumn,
className = '',
}: ColumnSelectorProps) {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
// Close dropdown when clicking outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node) &&
buttonRef.current &&
!buttonRef.current.contains(event.target as Node)
) {
setIsOpen(false);
}
};
if (isOpen) {
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}
}, [isOpen]);
const visibleCount = visibleColumns.size;
const totalCount = columns.length;
return (
<div className={`relative ${className}`}>
<button
ref={buttonRef}
type="button"
onClick={() => setIsOpen(!isOpen)}
className="inline-flex items-center gap-2 px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700 dark:hover:bg-gray-700 transition-colors"
>
<svg
className="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
<span>Columns</span>
<span className="text-xs text-gray-500 dark:text-gray-400">
({visibleCount}/{totalCount})
</span>
<ChevronDownIcon className={`w-4 h-4 transition-transform ${isOpen ? 'rotate-180' : ''}`} />
</button>
{isOpen && (
<div
ref={dropdownRef}
className="absolute right-0 mt-2 w-56 rounded-lg border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-800 z-50 max-h-96 overflow-y-auto"
>
<div className="p-2">
<div className="px-3 py-2 text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide border-b border-gray-200 dark:border-gray-700 mb-1">
Show Columns
</div>
<div className="space-y-1">
{columns.map((column) => {
const isVisible = visibleColumns.has(column.key);
return (
<label
key={column.key}
className="flex items-center gap-2 px-3 py-2 rounded-md hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer"
>
<Checkbox
checked={isVisible}
onChange={() => {
onToggleColumn(column.key);
}}
/>
<span className="text-sm text-gray-700 dark:text-gray-300 flex-1">
{column.label}
</span>
</label>
);
})}
</div>
<div className="mt-2 pt-2 border-t border-gray-200 dark:border-gray-700">
<button
type="button"
onClick={() => {
// Show all columns
columns.forEach((col) => {
if (!visibleColumns.has(col.key)) {
onToggleColumn(col.key);
}
});
}}
className="w-full px-3 py-1.5 text-xs font-medium text-brand-600 hover:text-brand-700 hover:bg-brand-50 dark:text-brand-400 dark:hover:bg-brand-500/10 rounded-md transition-colors"
>
Show All
</button>
</div>
</div>
</div>
)}
</div>
);
}