UNPKG

react-data-table-tailwind

Version:
86 lines (85 loc) 6.15 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import classNames from "classnames"; import { useEffect, useMemo, useRef, useState } from "react"; import Spinner from "./components/Spinner"; import { debounce } from "loadsh"; export default function DataTable({ columns, data, loading, usePagination, totalPages, onPageChange, dark = false, containerClassName, }) { const [currentPage, setCurrentPage] = useState(1); const [filteredData, setFilteredData] = useState(data); const [searchValues, setSearchValues] = useState({}); const inputRefs = useRef({}); const focusedInput = useRef(null); useEffect(() => { setFilteredData(data); }, [data]); useEffect(() => { if (focusedInput.current && inputRefs.current[focusedInput.current]) { inputRefs.current[focusedInput.current]?.focus(); } }); const debouncedSearch = useMemo(() => debounce((accessor, value) => { const column = columns.find((col) => col.accessor === accessor); if (column && column.onSearch) { column.onSearch(accessor, value); } else { const filteredData = data.filter((item) => String(item[accessor]) .toLowerCase() .includes(value.toLowerCase())); if (onPageChange) { onPageChange(1); // Reset to first page on search } setFilteredData(filteredData); } }, 300), // 300ms debounce delay [columns, data, onPageChange]); const handleSearchChange = (accessor, value) => { focusedInput.current = accessor; setSearchValues((prev) => ({ ...prev, [accessor]: value })); debouncedSearch(accessor, value); }; const handlePageChange = (page) => { setCurrentPage(page); if (onPageChange) { onPageChange(page); } }; return (_jsx("div", { className: classNames(containerClassName, "mt-8 p-4 rounded-lg shadow-md", { "bg-gray-800": dark, "bg-white": !dark, }), children: _jsx("div", { className: "w-full", style: { overflowX: "auto" }, children: loading ? (_jsx(Spinner, {})) : (_jsxs(_Fragment, { children: [_jsxs("table", { className: classNames("w-full divide-y", { "divide-gray-700": dark, "divide-gray-200": !dark, }), children: [_jsx("thead", { className: classNames("rounded-lg", { "bg-gray-700": dark, "bg-gray-200": !dark, }), children: _jsx("tr", { children: columns.map((column) => (_jsx("th", { className: classNames("px-6 py-3 text-left text-xs font-medium uppercase tracking-wider", { "text-gray-300": dark, "text-gray-700": !dark, }), children: _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { children: column.header }), column.searchable && (_jsx("input", { ref: (el) => { inputRefs.current[column.accessor] = el; }, type: "text", placeholder: `Search ${column.header}`, value: searchValues[String(column.accessor)] || "", onChange: (e) => handleSearchChange(column.accessor, e.target.value), className: classNames("mt-2 px-2 py-1 text-sm rounded border", { "bg-gray-900 text-gray-300 border-gray-600": dark, "bg-gray-100 text-gray-700 border-gray-300": !dark, }) }))] }) }, String(column.accessor)))) }) }), _jsx("tbody", { className: classNames({ "bg-gray-800 divide-y divide-gray-700": dark, "bg-white divide-y divide-gray-200": !dark, }), children: filteredData.length === 0 ? (_jsx("tr", { children: _jsx("td", { colSpan: columns.length, className: classNames("px-6 py-4 whitespace-nowrap text-center text-sm", { "text-gray-400": dark, "text-gray-500": !dark, }), children: "No data found" }) })) : (filteredData.map((item, index) => (_jsx("tr", { children: columns.map((column) => (_jsx("td", { className: classNames("px-6 py-4 whitespace-nowrap text-sm", { "text-gray-300": dark, "text-gray-700": !dark, }), children: column.renderRow ? column.renderRow(item, index) : String(item[column.accessor]) }, column.accessor))) }, index)))) })] }), usePagination && (_jsxs("div", { className: "flex justify-between items-center mt-4", children: [_jsx("button", { onClick: () => handlePageChange(currentPage - 1), disabled: currentPage === 1, className: classNames("px-4 py-2 rounded disabled:opacity-50", { "bg-gray-600 text-white": dark, "bg-gray-200 text-gray-700": !dark, }), children: "Previous" }), _jsxs("span", { className: classNames({ "text-white": dark, "text-gray-700": !dark, }), children: ["Page ", currentPage, " of ", totalPages] }), _jsx("button", { onClick: () => handlePageChange(currentPage + 1), disabled: currentPage === totalPages, className: classNames("px-4 py-2 rounded disabled:opacity-50", { "bg-gray-600 text-white": dark, "bg-gray-200 text-gray-700": !dark, }), children: "Next" })] }))] })) }) })); }