UNPKG

my-datatable-sydyjo

Version:

A React table to manage and display employees

157 lines (141 loc) 5.38 kB
import { useState, useMemo } from 'react' import TableHeader from '../TableHeader' import SearchBar from '../SearchBar' import RowsPerPageSelector from '../RowsPerPageSelector' import Pagination from '../Pagination' import '../../styles/DataTable.css' /** * DataTable component that displays data in a table with sorting, searching, pagination, * and adjustable rows per page. * @param {Array} data - Array of objects representing the table data */ function DataTable({ data }) { // Current page number for pagination const [currentPage, setCurrentPage] = useState(1) // Number of rows to display per page const [rowsPerPage, setRowsPerPage] = useState(10) // Search term used to filter table data const [searchTerm, setSearchTerm] = useState('') // Currently sorted column accessor key const [sortColumn, setSortColumn] = useState(null) // Sorting direction: 'asc' or 'desc' const [sortDirection, setSortDirection] = useState('asc') /** * Format a date string to French locale date format (dd/mm/yyyy) * @param {string} dateString - ISO date string * @returns {string} - formatted date string */ const formatDate = (dateString) => { const date = new Date(dateString) return date.toLocaleDateString('fr-FR') } // Define table columns: header label, data key accessor, and optional render function const columns = [ { header: 'First Name', accessor: 'firstName' }, { header: 'Last Name', accessor: 'lastName' }, { header: 'Star Date', accessor: 'startDate', render: formatDate }, { header: 'Department', accessor: 'department' }, { header: 'Date of Birth', accessor: 'dateOfBirth', render: formatDate }, { header: 'Street', accessor: 'street' }, { header: 'City', accessor: 'city' }, { header: 'State', accessor: 'state' }, { header: 'Zip Code', accessor: 'zipCode' }, ] /** * Processes the data by filtering based on the search term and sorting * according to the selected column and direction. * useMemo optimizes by recalculating only when dependencies change. */ const processedData = useMemo(() => { let tempData = [...data] if (searchTerm) { const lower = searchTerm.toLowerCase() tempData = tempData.filter((item) => Object.values(item).some((val) => val?.toString().toLowerCase().includes(lower), ), ) } if (sortColumn) { tempData.sort((a, b) => { const valA = a[sortColumn] const valB = b[sortColumn] if (valA == null) return 1 if (valB == null) return -1 let result = 0 if (typeof valA === 'string') { result = valA.localeCompare(valB) } else { if (valA > valB) { result = 1 } else if (valA < valB) { result = -1 } else { result = 0 } } return result }) if (sortDirection === 'desc') tempData.reverse() } return tempData }, [data, searchTerm, sortColumn, sortDirection]) useMemo(() => setCurrentPage(1), [searchTerm, rowsPerPage]) const paginatedData = useMemo(() => { const start = (currentPage - 1) * rowsPerPage return processedData.slice(start, start + rowsPerPage) }, [processedData, currentPage, rowsPerPage]) const totalPages = Math.ceil(processedData.length / rowsPerPage) /** * Handles sorting when a column header is clicked. * If the same column is clicked again, toggles the sort direction. * Otherwise, sets sorting to the new column in ascending order. * @param {string} accessor - column key to sort by */ const handleSort = (accessor) => { if (sortColumn === accessor) { setSortDirection((prev) => (prev === 'asc' ? 'desc' : 'asc')) } else { setSortColumn(accessor) setSortDirection('asc') } } return ( <div className="dataTables_wrapper no-footer"> <RowsPerPageSelector value={rowsPerPage} onChange={setRowsPerPage} /> <SearchBar value={searchTerm} onChange={setSearchTerm} /> <table className="display dataTable no-footer"> <TableHeader columns={columns} sortColumn={sortColumn} sortDirection={sortDirection} onSort={handleSort} /> <tbody> {paginatedData.map((row, i) => ( <tr key={i} className={i % 2 === 0 ? 'odd' : 'even'}> {columns.map((col) => ( <td key={col.accessor} className={col.accessor === sortColumn ? 'sorting_1' : ''} > {col.render ? col.render(row[col.accessor]) : row[col.accessor]} </td> ))} </tr> ))} </tbody> </table> <div className="dataTables_info">{`Showing ${currentPage} to ${totalPages} of ${processedData.length} entries`}</div> <Pagination currentPage={currentPage} totalPages={totalPages} onPrevious={() => setCurrentPage((p) => Math.max(p - 1, 1))} onNext={() => setCurrentPage((p) => Math.min(p + 1, totalPages))} /> </div> ) } export default DataTable