import React, { useState, useRef, useEffect, useCallback } from 'react'; import { AnimatePresence, motion } from 'framer-motion'; import { BaseTableData, TableColumn } from '../../types'; import { dropdownVariants, columnItemVariants } from '../../motions/motion'; // Import icons import { Columns, Check } from '../../icons'; import useOnClickOutside from '../../hooks/useOnClickOutside'; interface ColumnVisibilityToggleProps { /** * All available columns */ columns: TableColumn[]; /** * Currently visible columns */ visibleColumns?: TableColumn[]; /** * Callback when toggling a column's visibility */ onToggleColumn: (columnId: string) => void; /** * Callback to show all columns */ onShowAllColumns?: () => void; /** * Callback to hide all columns */ onHideAllColumns?: () => void; /** * Callback to reset columns to default visibility */ onResetColumns?: () => void; /** * Class name for the component */ className?: string; /** * Alternative method to check if a column is visible using columnVisibility object */ columnVisibility?: Record; } export function ColumnVisibilityToggle({ columns, visibleColumns = [], onToggleColumn, onShowAllColumns, onHideAllColumns, onResetColumns, className, columnVisibility }: ColumnVisibilityToggleProps) { // State const [isOpen, setIsOpen] = useState(false); const [localVisibility, setLocalVisibility] = useState>({}); const [isMounted, setIsMounted] = useState(false); // Refs const dropdownRef = useRef(null); // Initialize local visibility state from props useEffect(() => { if (columnVisibility) { setLocalVisibility({...columnVisibility}); } else if (visibleColumns.length > 0 && columns.length > 0) { const visibilityMap: Record = {}; columns.forEach(column => { const columnId = column.id || column.accessorKey as string; if (columnId) { visibilityMap[columnId] = visibleColumns.some( (col) => (col.id || col.accessorKey as string) === columnId ); } }); setLocalVisibility(visibilityMap); } }, [columnVisibility, visibleColumns, columns]); // Mark component as mounted (for hydration safety) useEffect(() => { setIsMounted(true); }, []); // Close dropdown when clicking outside useOnClickOutside(dropdownRef, () => setIsOpen(false)); // Close on ESC key useEffect(() => { const handleEscKey = (event: KeyboardEvent) => { if (event.key === 'Escape' && isOpen) { setIsOpen(false); } }; document.addEventListener('keydown', handleEscKey); return () => { document.removeEventListener('keydown', handleEscKey); }; }, [isOpen]); // Check if a column is visible - supports both methods const isColumnVisible = useCallback((column: TableColumn): boolean => { const columnId = column.id || column.accessorKey as string; // If columnVisibility object is provided, use it if (columnVisibility) { return columnVisibility[columnId] !== false; } // Otherwise check in visibleColumns array return visibleColumns.some( (col) => (col.id || col.accessorKey as string) === columnId ); }, [columnVisibility, visibleColumns]); // Handle toggle column directly for better responsiveness const handleToggleColumn = useCallback((columnId: string) => { // Update local state immediately for responsive UI setLocalVisibility(prev => ({ ...prev, [columnId]: !prev[columnId] })); // Call the parent callback onToggleColumn(columnId); }, [onToggleColumn]); // Handle hide all columns const handleHideAll = useCallback(() => { // Update local state immediately const newVisibility: Record = {}; columns.forEach(column => { const columnId = column.id || column.accessorKey as string; if (columnId) { newVisibility[columnId] = false; } }); setLocalVisibility(newVisibility); // Call parent callback if (onHideAllColumns) { onHideAllColumns(); } setIsOpen(false); }, [columns, onHideAllColumns]); // Handle show all columns const handleShowAll = useCallback(() => { // Update local state immediately const newVisibility: Record = {}; columns.forEach(column => { const columnId = column.id || column.accessorKey as string; if (columnId) { newVisibility[columnId] = true; } }); setLocalVisibility(newVisibility); // Call parent callback if (onShowAllColumns) { onShowAllColumns(); } setIsOpen(false); }, [columns, onShowAllColumns]); // Handle reset columns const handleReset = useCallback(() => { if (onResetColumns) { onResetColumns(); } setIsOpen(false); }, [onResetColumns]); // Calculate hidden columns count - only client-side to avoid hydration mismatch const hiddenColumnsCount = isMounted ? columns.filter(col => !isColumnVisible(col)).length : 0; return (
{/* Toggle button */} {/* Dropdown menu */} {isOpen && ( {/* Header */}

Toggle columns

{onResetColumns && ( )}
{/* Column list with custom scrollbar */}
{columns.length === 0 ? (

No columns available

) : ( columns.map((column, index) => { const columnId = column.id || column.accessorKey as string; // Use local visibility state for immediate feedback const isVisible = columnId ? (localVisibility[columnId] !== undefined ? localVisibility[columnId] : isColumnVisible(column) ) : true; const label = typeof column.header === 'string' ? column.header : columnId; if (!columnId) return null; return ( ); }) )}
)}
); } export default ColumnVisibilityToggle;