Created
April 25, 2024 01:36
-
-
Save MohitS69/efd93f9b8a5dc29792612b30e820629a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Dispatch, SetStateAction, useEffect, useRef, useState } from "react"; | |
import { | |
FilterFn, | |
Header, | |
createColumnHelper, | |
flexRender, | |
getCoreRowModel, | |
getFilteredRowModel, | |
getSortedRowModel, | |
useReactTable, | |
} from "@tanstack/react-table"; | |
import { Checkbox, Paper, Popper, IconButton, Chip } from "@mui/material"; | |
import { rankItem } from "@tanstack/match-sorter-utils"; | |
import styles from "./App.module.css"; | |
import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward"; | |
import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward"; | |
import ClearIcon from "@mui/icons-material/Clear"; | |
import SwapVertIcon from "@mui/icons-material/SwapVert"; | |
type Props = {}; | |
type TProductColumn = { | |
partNumber: string; | |
family: string; | |
description: string; | |
}; | |
const mockData = [ | |
{ | |
partNumber: "12345", | |
family: "Electronics", | |
description: "Smartphone", | |
}, | |
{ | |
partNumber: "67890", | |
family: "Clothing", | |
description: "T-Shirt", | |
}, | |
{ | |
partNumber: "54321", | |
family: "Home Appliances", | |
description: "Microwave Oven", | |
}, | |
{ | |
partNumber: "98765", | |
family: "Furniture", | |
description: "Sofa", | |
}, | |
{ | |
partNumber: "13579", | |
family: "Sporting Goods", | |
description: "Basketball", | |
}, | |
]; | |
const columnHelper = createColumnHelper<TProductColumn>(); | |
const columnsAutoCompletionWithTable = [ | |
columnHelper.display({ | |
id: "selection", | |
header: ({ table }) => ( | |
<Checkbox | |
checked={table.getIsAllRowsSelected()} | |
indeterminate={table.getIsSomeRowsSelected()} | |
onChange={table.getToggleAllRowsSelectedHandler()} | |
/> | |
), | |
cell: ({ row }) => ( | |
<Checkbox | |
onChange={row.getToggleSelectedHandler()} | |
checked={row.getIsSelected()} | |
sx={{ zIndex: "10" }} | |
/> | |
), | |
}), | |
columnHelper.accessor("partNumber", { | |
id: "partNumber", | |
header: "Part Number", | |
}), | |
columnHelper.accessor("family", { | |
id: "family", | |
header: "Family", | |
}), | |
columnHelper.accessor("description", { | |
id: "description", | |
header: "Description", | |
}), | |
]; | |
const fuzzyFilter: FilterFn<TProductColumn> = ( | |
row, | |
columnId, | |
value, | |
addMeta, | |
) => { | |
const itemRank = rankItem(row.getValue(columnId), value); | |
addMeta({ itemRank }); | |
return itemRank.passed; | |
}; | |
function SelectProduct({}: Props) { | |
const [selectedRows, setSelectedRows] = useState<TProductColumn[]>([]); | |
return ( | |
<div | |
style={{ | |
height: "400px", | |
width: "510px", | |
}} | |
> | |
<h1 className={styles.mainHeading}>Select Products</h1> | |
<h4 className={styles.boldHeading}>Select Products</h4> | |
<p> | |
Select products you would like the group to spend these credits | |
on | |
</p> | |
<AutoCompleteWithTable setSelectedRows={setSelectedRows} /> | |
<FilteredTable data={selectedRows} /> | |
</div> | |
); | |
} | |
export default SelectProduct; | |
function AutoCompleteWithTable({ | |
setSelectedRows, | |
}: { | |
setSelectedRows: Dispatch<SetStateAction<TProductColumn[]>>; | |
}) { | |
const [open, setOpen] = useState(false); | |
const [selected, setSelected] = useState<TProductColumn[]>([]); | |
const inputRef = useRef<null | HTMLInputElement>(null); | |
const table = useReactTable<(typeof mockData)[number]>({ | |
data: mockData, | |
columns: columnsAutoCompletionWithTable, | |
getCoreRowModel: getCoreRowModel(), | |
enableRowSelection: true, | |
filterFns: { | |
fuzzy: fuzzyFilter, | |
}, | |
globalFilterFn: fuzzyFilter, | |
getFilteredRowModel: getFilteredRowModel(), | |
}); | |
useEffect(() => { | |
const obj = table.getSelectedRowModel().rowsById; | |
const arr = []; | |
for (let key in obj) { | |
const item = obj[key]; | |
arr.push({ | |
...item.original, | |
id: key, | |
}); | |
} | |
setSelected(arr); | |
}, [table.getSelectedRowModel().rows]); | |
return ( | |
<> | |
<div className={styles.inputWrapper} ref={inputRef}> | |
{selected.map((item) => ( | |
<Chip | |
label={item.partNumber} | |
key={item.partNumber} | |
onDelete={() => { | |
table.setRowSelection((old) => { | |
const obj = {...old} | |
obj[item.id] = false | |
return obj | |
}); | |
}} | |
/> | |
))} | |
<input | |
onFocus={() => setOpen(true)} | |
placeholder="Search by Part Number,Family or Description" | |
onChange={(e) => table.setGlobalFilter(e.target.value)} | |
className={styles.searchInput} | |
/> | |
</div> | |
<Popper | |
disablePortal | |
open={open} | |
anchorEl={inputRef.current} | |
sx={{ zIndex: "1400" }} | |
> | |
<Paper elevation={4} sx={{ width: "510px", padding: "4px" }}> | |
<div | |
style={{ | |
overflowY: "scroll", | |
overflowX: "hidden", | |
maxHeight: "200px", | |
}} | |
> | |
<table | |
style={{ | |
width: "100%", | |
borderCollapse: "collapse", | |
}} | |
> | |
<thead | |
style={{ | |
position: "sticky", | |
top: 0, | |
backgroundColor: "white", | |
zIndex: "200", | |
}} | |
> | |
{table.getHeaderGroups().map((headerGroup) => ( | |
<tr key={headerGroup.id}> | |
{headerGroup.headers.map((header) => ( | |
<th | |
key={header.id} | |
style={{ textAlign: "start" }} | |
> | |
{flexRender( | |
header.column.columnDef | |
.header, | |
header.getContext(), | |
)} | |
</th> | |
))} | |
</tr> | |
))} | |
</thead> | |
<tbody> | |
{table.getRowModel().rows.map((row) => { | |
return ( | |
<tr key={row.id}> | |
{row | |
.getVisibleCells() | |
.map((cell) => { | |
return ( | |
<td | |
key={cell.id} | |
style={{ | |
textAlign: | |
"start", | |
}} | |
> | |
{flexRender( | |
cell.column | |
.columnDef | |
.cell, | |
cell.getContext(), | |
)} | |
</td> | |
); | |
})} | |
</tr> | |
); | |
})} | |
</tbody> | |
</table> | |
</div> | |
<div | |
style={{ | |
textAlign: "end", | |
marginTop: "4px", | |
}} | |
> | |
<button | |
className={styles.clearProductButton} | |
onClick={() => table.resetRowSelection()} | |
> | |
Clear | |
</button> | |
<button | |
className={styles.addProductButton} | |
onClick={() => { | |
setSelectedRows([...selected]); | |
setOpen(false); | |
}} | |
> | |
Add ({table.getSelectedRowModel().rows.length}) | |
</button> | |
</div> | |
</Paper> | |
</Popper> | |
</> | |
); | |
} | |
function FilteredTable({ data }: { data: TProductColumn[] }) { | |
const [list, setList] = useState<TProductColumn[]>([]); | |
const table = useReactTable<(typeof mockData)[number]>({ | |
data: list, | |
columns: [ | |
columnHelper.accessor("partNumber", { | |
id: "partNumber", | |
header: "Part Number", | |
}), | |
columnHelper.accessor("family", { | |
id: "family", | |
header: "Family", | |
}), | |
columnHelper.accessor("description", { | |
id: "description", | |
header: "Description", | |
}), | |
columnHelper.display({ | |
id: "deletion", | |
cell: ({ row }) => ( | |
<IconButton | |
aria-label="delete" | |
onClick={() => | |
setList((prev) => | |
prev.filter( | |
(item) => | |
item.partNumber !== | |
row.original.partNumber, | |
), | |
) | |
} | |
> | |
<ClearIcon /> | |
</IconButton> | |
), | |
}), | |
], | |
getCoreRowModel: getCoreRowModel(), | |
getSortedRowModel: getSortedRowModel(), | |
}); | |
useEffect(() => { | |
setList([...data]); | |
}, [data]); | |
return ( | |
<div | |
style={{ | |
overflowY: "scroll", | |
overflowX: "hidden", | |
maxHeight: "200px", | |
}} | |
> | |
<table style={{ width: "100%", borderCollapse: "collapse" }}> | |
<thead | |
style={{ | |
position: "sticky", | |
top: 0, | |
backgroundColor: "white", | |
zIndex: "200", | |
}} | |
> | |
{table.getHeaderGroups().map((headerGroup) => ( | |
<tr key={headerGroup.id}> | |
{headerGroup.headers.map((header, index) => | |
index !== 3 ? ( | |
<TableHeader | |
header={header} | |
key={header.id} | |
/> | |
) : null, | |
)} | |
</tr> | |
))} | |
</thead> | |
<tbody> | |
{table.getRowModel().rows.map((row) => { | |
return ( | |
<tr key={row.id}> | |
{row.getVisibleCells().map((cell) => { | |
return ( | |
<td | |
key={cell.id} | |
style={{ textAlign: "start" }} | |
> | |
{flexRender( | |
cell.column.columnDef.cell, | |
cell.getContext(), | |
)} | |
</td> | |
); | |
})} | |
</tr> | |
); | |
})} | |
</tbody> | |
</table> | |
</div> | |
); | |
} | |
const TableHeader = ({ | |
header, | |
}: { | |
header: Header<TProductColumn, unknown>; | |
}) => { | |
const isSorted = header.column.getIsSorted(); | |
return ( | |
<th> | |
<div | |
style={{ | |
display: "flex", | |
alignItems: "center", | |
justifyContent: "center", | |
gap: "4px", | |
}} | |
> | |
<p> | |
{flexRender( | |
header.column.columnDef.header, | |
header.getContext(), | |
)} | |
</p> | |
<span | |
onClick={header.column.getToggleSortingHandler()} | |
style={{ | |
cursor: "pointer", | |
}} | |
> | |
{isSorted === "asc" ? ( | |
<ArrowUpwardIcon fontSize="small" /> | |
) : isSorted === "desc" ? ( | |
<ArrowDownwardIcon fontSize="small" /> | |
) : ( | |
<SwapVertIcon fontSize="small" /> | |
)} | |
</span> | |
</div> | |
</th> | |
); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment