132 lines
4.4 KiB
TypeScript
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>
|
|
);
|
|
}
|
|
|