Skip to content

Instantly share code, notes, and snippets.

@MohitS69
Created April 25, 2024 01:36
Show Gist options
  • Save MohitS69/efd93f9b8a5dc29792612b30e820629a to your computer and use it in GitHub Desktop.
Save MohitS69/efd93f9b8a5dc29792612b30e820629a to your computer and use it in GitHub Desktop.
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