UNPKG

react-beautiful-grid-shahab

Version:

346 lines (325 loc) 14.1 kB
/** @format */ import React, { useState } from "react"; import { BsThreeDotsVertical } from "react-icons/bs"; import { MdFilterListAlt } from "react-icons/md"; import { BsPaperclip } from "react-icons/bs"; import { IoMdArrowDropup } from "react-icons/io"; import { IoMdArrowDropdown } from "react-icons/io"; import { PiLessThan } from "react-icons/pi"; import { PiGreaterThan } from "react-icons/pi"; import { BsSliders } from "react-icons/bs"; import FilterModal from "../modals/FilterModal"; import SortedModal from "../modals/SortedModal"; import DeleteModal from "../modals/DeleteModal"; const PaginatedTable = ({ data, columns: initialColumns, headerBackgroundColor, }) => { const [currentPage, setCurrentPage] = useState(1); const [selectedRows, setSelectedRows] = useState([]); const [columns, setColumns] = useState([...new Set(initialColumns)]); const [showModal, setShowModal] = useState(false); const [filterSearch, setFilterSearch] = useState(""); const [sortedColumn, setSortedColumn] = useState(null); const [sortedData, setSortedData] = useState(data); const [modalData, setModalData] = useState([]); const [showFilterModal, setShowFilterModal] = useState(false); const [showSortedModal, setShowSortedModal] = useState(false); const [openDeleteModal, setOpenDeleteModal] = useState(false); const [modalPosition, setModalPosition] = useState({ x: 0, y: 0 }); const [sortDirection, setSortDirection] = useState({}); const rowsPerPage = 10; const totalPages = Math.ceil(sortedData.length / rowsPerPage); const currentRows = sortedData.slice( (currentPage - 1) * rowsPerPage, currentPage * rowsPerPage ); // <--------------------- Sorting logic ------------------------> const handleSort = (columnName, direction) => { const sorted = [...sortedData].sort((a, b) => { if (direction === "asc") { return a[columnName] > b[columnName] ? 1 : -1; } else { return a[columnName] < b[columnName] ? 1 : -1; } }); setSortedColumn(columnName); setSortedData(sorted); setSortDirection({ [columnName]: direction }); // Update the direction for the column }; // <-------------------- OPEN AND CLOSE MODALS LOGIC --------------------------> const handleFilterClick = () => { setShowFilterModal(true); }; const openSortModal = (columnName, index) => { const uniqueValues = [...new Set(data.map((item) => item[columnName]))]; setModalData(uniqueValues); setShowSortedModal(showSortedModal === index ? null : index); // Open the sorted modal }; const closeModal = () => { setShowFilterModal(false); setShowSortedModal(false); }; // <-------------------- OPEN AND CLOSE MODALS FILTER LOGIC --------------------------> const openFilterModal = (columnName) => { const uniqueValues = [...new Set(data.map((item) => item[columnName]))]; setModalData(uniqueValues); setSortedColumn(columnName); setShowSortedModal(true); }; const applyFilter = (selectedValues) => { if (sortedColumn) { const filteredData = data.filter((row) => selectedValues.includes(row[sortedColumn]) ); setSortedData(filteredData); } closeModal(); }; const clearFilter = () => { setSortedData(data); setSortedColumn(null); }; // <-------------------- ADD AND DELETE COLUMN ON CHECKBOX --------------------------> const handleColumnToggle = (columnName, checked) => { const columnIndex = initialColumns.indexOf(columnName); if (checked) { const updatedColumns = [...columns]; updatedColumns.splice(columnIndex, 0, columnName); setColumns(updatedColumns); } else { setColumns((prev) => prev.filter((col) => col !== columnName)); } }; // <-------------------------- FUNCTION FOR DRAG AND DROP ---------------------------> const handleDragStart = (e, index) => { e.dataTransfer.setData("draggedIndex", index); }; const handleDrop = (e, index) => { const draggedIndex = e.dataTransfer.getData("draggedIndex"); const updatedColumns = [...columns]; const draggedColumn = updatedColumns.splice(draggedIndex, 1)[0]; updatedColumns.splice(index, 0, draggedColumn); setColumns(updatedColumns); }; const handleInputChange = (rowId, column, value) => { setSortedData((prevData) => prevData.map((row) => row.id === rowId ? { ...row, [column]: value } : row ) ); }; // <--------------------- For Selecting all checkbox on One -------------------------> const handleSelectAll = (checked) => { if (checked) { const allCurrentRowIds = currentRows.map((row) => row.id); setSelectedRows([...new Set([...selectedRows, ...allCurrentRowIds])]); } else { const remainingRows = selectedRows.filter( (id) => !currentRows.map((row) => row.id).includes(id) ); setSelectedRows(remainingRows); } }; // <--------------------- For Selecting all checkbox individually -------------------------> const handleRowSelection = (id, checked) => { if (checked) { setSelectedRows((prev) => [...prev, id]); } else { setSelectedRows((prev) => prev.filter((rowId) => rowId !== id)); } }; const handleOpenDeleteModal = (id) => { setModalPosition({ x: event.clientX, y: event.clientY }); setOpenDeleteModal(true); // Set the index of the icon }; const handleCloseDeleteModal = () => { setOpenDeleteModal(false); // Close the modal }; return ( <> <section className='p-4 '> <div className=' overflow-y-auto'> <div className='overflow-auto max-h-[400px] border border-gray-300 rounded-lg'> <table className='w-full border-collapse table-fixed'> <thead> <tr className={`${headerBackgroundColor} sticky top-0 z-20`}> {/* First Header Cell - Sticky Horizontally and Vertically */} <th className='bg-blue-700 border border-gray-300 px-4 py-2 text-left w-[50px] sticky top-0 left-0 z-30'> <input type='checkbox' onChange={(e) => handleSelectAll(e.target.checked)} checked={currentRows.every((row) => selectedRows.includes(row.id) )} /> </th> {/* Second Header Cell - Sticky Horizontally and Vertically */} <th className='bg-blue-700 border border-gray-300 px-4 py-2 w-[80px] sticky top-0 left-[50px] z-30'> <button onClick={handleFilterClick}> <BsSliders className='w-6 h-6 text-white' /> </button> </th> {/* Other Headers */} {columns.map((col, index) => ( <th key={index} className='border border-gray-300 px-4 py-2 text-white w-[150px] sticky top-0 z-10' draggable onDragStart={(e) => handleDragStart(e, index)} onDragOver={(e) => e.preventDefault()} onDrop={(e) => handleDrop(e, index)} style={{ cursor: "move" }}> <div className='flex items-center justify-between'> <span onClick={() => handleSort(col)}>{col}</span> <div className='flex items-center space-x-2'> {(col === "Caller Name" || col === "Division" || col === "Reg No" || col === "Full Name" || col === "Duty") && ( <div> <IoMdArrowDropup className={`ml-2 font-bold cursor-pointer ${ sortDirection[col] === "asc" ? "text-gray-700" : "text-white" }`} onClick={() => handleSort(col, "asc")} /> <IoMdArrowDropdown className={`ml-2 font-bold cursor-pointer ${ sortDirection[col] === "desc" ? "text-gray-700" : "text-white" }`} onClick={() => handleSort(col, "desc")} /> </div> )} {col === "Encounter Time" && ( <button onClick={() => openFilterModal(col, index)} className='ml-2'> <MdFilterListAlt className='w-4 h-4 relative' /> </button> )} {col === "Division" && ( <button onClick={() => handleSort(col)} className='ml-2'> <MdFilterListAlt className='w-4 h-4 relative' /> </button> )} </div> </div> </th> ))} </tr> </thead> <tbody> {currentRows.map((row, id) => ( <tr key={row.id}> {/* First Sticky Cell - Body */} <td className='left-0 bg-gray-100 border border-gray-300 px-4 py-2 w-[50px] sticky top-0 z-10'> <input type='checkbox' checked={selectedRows.includes(row.id)} onChange={(e) => handleRowSelection(row.id, e.target.checked) } /> </td> {/* Second Sticky Cell - Body */} <td className='left-[50px] bg-gray-100 border border-gray-300 px-4 py-2 w-[80px] sticky top-0 z-10'> <div className='flex'> <BsPaperclip className='w-4 h-4 text-blue-700' /> <BsThreeDotsVertical className='w-4 h-4 cursor-pointer text-gray-500 relative' onClick={() => handleOpenDeleteModal(id)} /> </div> </td> {columns.map((col, colIndex) => ( <td key={colIndex} className={`border border-gray-300 px-4 py-2 w-[150px] ${ col === "Address" ? "truncate overflow-hidden whitespace-nowrap text-ellipsis" : "" }`}> {col === "Encounter No" ? ( <span> <input type='text' className='bg-transparent w-24 outline-none focus:outline-none' value={row[col] || ""} onChange={(e) => handleInputChange(row.id, col, e.target.value) } /> </span> ) : ( row[col] || "" )} </td> ))} </tr> ))} </tbody> </table> </div> </div> {/* <----------------------- PAGINATION --------------------------> */} <div className='mt-4 flex justify-end space-x-2'> <button onClick={() => setCurrentPage((prev) => Math.max(prev - 1, 1))}> <PiLessThan className='w-4 h-4 font-bold' /> </button> {Array.from({ length: totalPages }, (_, index) => ( <button key={index} onClick={() => setCurrentPage(index + 1)} className={`px-2 rounded ${ currentPage === index + 1 ? "border border-blue-500 text-gray-600" : "border border-blue-500" }`}> {index + 1} </button> ))} <button onClick={() => setCurrentPage((prev) => Math.min(prev + 1, totalPages)) }> <PiGreaterThan className='w-4 h-4 font-bold' /> </button> </div> {/* <--------------------------- SORTED MODAL ------------------------> */} <SortedModal showModal={showSortedModal} modalData={modalData} applyFilter={applyFilter} clearFilter={clearFilter} closeModal={closeModal} /> <FilterModal showModal={showFilterModal} filterSearch={filterSearch} setFilterSearch={setFilterSearch} columns={columns} initialColumns={initialColumns} handleColumnToggle={handleColumnToggle} closeModal={closeModal} /> <DeleteModal isOpen={openDeleteModal} handleClose={handleCloseDeleteModal} modalPosition={modalPosition} /> </section> </> ); }; export default PaginatedTable;