UNPKG

@hugo-lml/hr-net-table

Version:
1 lines 10.5 kB
{"version":3,"sources":["../src/CustomTable.tsx"],"sourcesContent":["import { ChangeEvent, useMemo, useState } from \"react\";\nimport \"./CustomTable.css\";\n\n// Column interface: describes a table column, with label, key, and optional sortable flag\ninterface Column<T> {\n label: string;\n key: keyof T;\n sortable?: boolean;\n}\n\n// Props for the CustomTable: array of data and columns\ninterface CustomTableProps<T> {\n data: T[];\n columns: Column<T>[];\n}\n\n// Generic CustomTable component\nconst CustomTable = <T extends Record<string, unknown>>({\n data,\n columns,\n}: CustomTableProps<T>) => {\n // State for sorting, pagination, and search\n const [sortKey, setSortKey] = useState<keyof T | null>(null);\n const [sortDirection, setSortDirection] = useState<\"asc\" | \"desc\">(\"asc\");\n const [rowsPerPage, setRowsPerPage] = useState<number>(10);\n const [currentPage, setCurrentPage] = useState<number>(1);\n const [searchQuery, setSearchQuery] = useState<string>(\"\");\n\n // Filter data based on search query\n const filteredData = useMemo(() => {\n if (!searchQuery) return data;\n\n return data.filter((row) =>\n columns.some((col) => {\n const cellValue = row[col.key];\n // If value is a Date, format it before searching\n if (cellValue instanceof Date) {\n return new Intl.DateTimeFormat(\"en-us\")\n .format(cellValue)\n .toLowerCase()\n .includes(searchQuery.toLowerCase());\n }\n // Otherwise, convert to string and search\n return String(cellValue)\n .toLowerCase()\n .includes(searchQuery.toLowerCase());\n })\n );\n }, [data, columns, searchQuery]);\n\n // Sort filtered data based on sortKey and sortDirection\n const sortedData = useMemo(() => {\n if (!sortKey) return filteredData;\n return [...filteredData].sort((a, b) => {\n const aValue = a[sortKey] as string | number | Date;\n const bValue = b[sortKey] as string | number | Date;\n\n // Handle Date sorting\n if (aValue instanceof Date || bValue instanceof Date) {\n return sortDirection === \"asc\"\n ? new Date(aValue).getTime() - new Date(bValue).getTime()\n : new Date(bValue).getTime() - new Date(aValue).getTime();\n }\n\n // String/number sorting\n return sortDirection === \"asc\"\n ? String(aValue).localeCompare(String(bValue))\n : String(bValue).localeCompare(String(aValue));\n });\n }, [filteredData, sortKey, sortDirection]);\n\n // Paginate sorted data\n const paginatedData = useMemo(() => {\n const startIndex = (currentPage - 1) * rowsPerPage;\n return sortedData.slice(startIndex, startIndex + rowsPerPage);\n }, [sortedData, currentPage, rowsPerPage]);\n\n // Calculate total number of pages\n const totalPages = Math.ceil(filteredData.length / rowsPerPage);\n\n // Handle sorting when a column header is clicked\n const handleSort = (key: keyof T) => {\n if (sortKey === key) {\n setSortDirection(sortDirection === \"asc\" ? \"desc\" : \"asc\");\n } else {\n setSortKey(key);\n setSortDirection(\"asc\");\n }\n };\n\n // Handle page change (pagination)\n const handlePageChange = (page: number) => {\n if (page < 1 || page > totalPages) return;\n setCurrentPage(page);\n };\n\n // Handle change in rows per page\n const handleRowsPerPageChange = (event: ChangeEvent<HTMLSelectElement>) => {\n setRowsPerPage(Number(event.target.value));\n setCurrentPage(1);\n };\n\n // Handle search input change\n const handleSearchChange = (event: ChangeEvent<HTMLInputElement>) => {\n setSearchQuery(event.target.value);\n setCurrentPage(1);\n };\n\n // No data to show\n if (data.length === 0) return <p>No data to show</p>\n\n return (\n <div className=\"custom-table-wrapper\">\n {/* Filter and search controls */}\n <div className=\"custom-table-filter\">\n <label htmlFor=\"rowsPerPage\">\n Show\n <select\n id=\"rowsPerPage\"\n value={rowsPerPage}\n onChange={handleRowsPerPageChange}\n >\n {[10, 25, 50, 100].map((size) => (\n <option key={size} value={size}>\n {size}\n </option>\n ))}\n </select>\n entries\n </label>\n <div>\n <label htmlFor=\"search\">Search:</label>\n <input\n id=\"search\"\n type=\"text\"\n value={searchQuery}\n onChange={handleSearchChange}\n placeholder=\"type to search...\"\n />\n </div>\n </div>\n\n {/* Table rendering */}\n <table className=\"custom-table\">\n <thead>\n <tr>\n {columns.map((column) => (\n <th\n key={String(column.key)}\n onClick={() => column.sortable && handleSort(column.key)}\n >\n <div>\n {column.label}\n {/* Sort icon */}\n {column.sortable && (\n <span\n className={`custom-table-sort-icon ${\n sortKey === column.key ? \"custom-table-sort-active\" : \"\"\n }`}\n >\n {sortDirection === \"asc\" ? \"\" : \"\"}\n </span>\n )}\n </div>\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {paginatedData.map((row, index) => (\n <tr key={index}>\n {columns.map((column) => (\n <td key={String(column.key)}>\n {/* Format Date values */}\n {row[column.key] instanceof Date\n ? new Intl.DateTimeFormat(\"en-us\").format(\n new Date(row[column.key] as Date)\n )\n : String(row[column.key])}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n\n {/* Pagination controls */}\n <div className=\"custom-table-pagination\">\n <span>\n Showing {(currentPage - 1) * rowsPerPage + 1} to{\" \"}\n {Math.min(currentPage * rowsPerPage, filteredData.length)} of{\" \"}\n {filteredData.length} entries\n </span>\n <div>\n Page {currentPage} of {totalPages}\n <button\n onClick={() => handlePageChange(currentPage - 1)}\n disabled={currentPage === 1}\n >\n Previous\n </button>\n <button\n onClick={() => handlePageChange(currentPage + 1)}\n disabled={currentPage === totalPages}\n >\n Next\n </button>\n </div>\n </div>\n </div>\n );\n};\n\nexport default CustomTable;\n"],"mappings":"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","names":[]}