Skip to content

Commit

Permalink
fix perf issues, added resizable columns
Browse files Browse the repository at this point in the history
  • Loading branch information
skhamis committed Jan 14, 2023
1 parent 820e852 commit 65033d9
Showing 1 changed file with 204 additions and 122 deletions.
326 changes: 204 additions & 122 deletions src/components/DynamicTableView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,81 @@ const columnsWithSubRows = [
"httpRealm",
];

const TableContainer = styled.div`
overflow-x: "auto";
`;
// styled-components allow us to create smaller customizable components
const StyledTable = styled.table`
border: 1px solid #8080804f;
border-radius: 4px;
margin-bottom: 4px;
tr {
width: fit-content;
height: 30px;
}
th {
position: relative;
}
td {
padding: 4px;
height: 30px;
position: relative;
}
tbody tr {
:nth-of-type(odd) {
background-color: #f0f0f0;
}
:hover {
background-color: lightgrey;
}
th {
padding: 2px 4px;
position: relative;
font-weight: bold;
text-align: center;
height: 30px;
}
} // end of tbody
.resizer {
position: absolute;
right: 0;
top: 0;
height: 100%;
width: 5px;
background: rgba(0, 0, 0, 0.5);
cursor: col-resize;
user-select: none;
touch-action: none;
}
td {
padding: 4px;
.resizer.isResizing {
background: blue;
opacity: 1;
}
@media (hover: hover) {
.resizer {
opacity: 0;
}
*:hover > .resizer {
opacity: 1;
}
`;

export default function DynamicTableView(props) {
const [columnFilters, setColumnFilters] = React.useState([]);
const [expanded, setExpanded] = React.useState({});
const [globalFilter, setGlobalFilter] = React.useState("");

let records = JSON.parse(stringify(props.data));
let records = props.data;

let tableKeys = new Set();
// Since each table comes with it's own schema, we just iterate over
Expand Down Expand Up @@ -104,6 +157,7 @@ export default function DynamicTableView(props) {
const table = useReactTable({
data,
columns,
columnResizeMode: "onChange",
state: {
columnFilters,
globalFilter,
Expand All @@ -115,8 +169,8 @@ export default function DynamicTableView(props) {
return row.tabs;
}

// If the row in question has an object we return it
// to be rendered by the subcomponent
// If the row has a nested object we return it
// so renderSubComponent can render it within the cell
for (const key of columnsWithSubRows) {
if (row[key]) {
return [row[key]];
Expand All @@ -137,144 +191,172 @@ export default function DynamicTableView(props) {

return (
<div className="p-2">
<div className="h-2" />
<StyledTable>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<th key={header.id} colSpan={header.colSpan}>
{header.isPlaceholder ? null : (
<>
<div>
{flexRender(
<TableContainer>
<StyledTable
{...{
style: {
width: table.getCenterTotalSize(),
},
}}
>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<th
{...{
key: header.id,
colSpan: header.colSpan,
style: {
width: header.getSize(),
},
}}
>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
<div
{...{
onMouseDown: header.getResizeHandler(),
onTouchStart: header.getResizeHandler(),
className: `resizer ${
header.column.getIsResizing() ? "isResizing" : ""
}`,
}}
/>
{header.column.getCanFilter() ? (
<div>
<Filter header={header} table={table} />
</div>
{header.column.getCanFilter() ? (
<div>
<Filter column={header.column} table={table} />
</div>
) : null}
</>
)}
</th>
);
})}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => {
return (
<Fragment key={row.id}>
<tr>
{row.getVisibleCells().map((cell) => {
return (
<td key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</td>
);
})}
</tr>
{row.getIsExpanded() && (
) : null}
</th>
);
})}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => {
return (
<Fragment key={row.id}>
<tr>
{/* 2nd row is a custom 1 cell row */}
<td colSpan={row.getVisibleCells().length}>
{renderSubComponent({ row })}
</td>
{row.getVisibleCells().map((cell) => {
return (
<td
{...{
key: cell.id,
style: {
width: cell.column.getSize(),
},
}}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</td>
);
})}
</tr>
)}
</Fragment>
);
})}
</tbody>
</StyledTable>
<div className="h-2" />
<div className="flex items-center gap-2">
<button
className="border rounded p-1"
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
{"<<"}
</button>
<button
className="border rounded p-1"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
{"<"}
</button>
<button
className="border rounded p-1"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
{">"}
</button>
<button
className="border rounded p-1"
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
{">>"}
</button>
<span className="flex items-center gap-1">
<div>Page</div>
<strong>
{table.getState().pagination.pageIndex + 1} of{" "}
{table.getPageCount()}
</strong>
</span>
<span className="flex items-center gap-1">
| Go to page:
<input
type="number"
defaultValue={table.getState().pagination.pageIndex + 1}
{row.getIsExpanded() && (
<tr>
{/* 2nd row is a custom 1 cell row */}
<td colSpan={row.getVisibleCells().length}>
{renderSubComponent({ row })}
</td>
</tr>
)}
</Fragment>
);
})}
</tbody>
</StyledTable>
<div className="h-2" />
<div className="flex items-center gap-2">
<button
className="border rounded p-1"
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
{"<<"}
</button>
<button
className="border rounded p-1"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
{"<"}
</button>
<button
className="border rounded p-1"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
{">"}
</button>
<button
className="border rounded p-1"
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
{">>"}
</button>
<span className="flex items-center gap-1">
<div>Page</div>
<strong>
{table.getState().pagination.pageIndex + 1} of{" "}
{table.getPageCount()}
</strong>
</span>
<span className="flex items-center gap-1">
| Go to page:
<input
type="number"
defaultValue={table.getState().pagination.pageIndex + 1}
onChange={(e) => {
const page = e.target.value ? Number(e.target.value) - 1 : 0;
table.setPageIndex(page);
}}
className="border p-1 rounded w-16"
/>
</span>
<select
value={table.getState().pagination.pageSize}
onChange={(e) => {
const page = e.target.value ? Number(e.target.value) - 1 : 0;
table.setPageIndex(page);
table.setPageSize(Number(e.target.value));
}}
className="border p-1 rounded w-16"
/>
</span>
<select
value={table.getState().pagination.pageSize}
onChange={(e) => {
table.setPageSize(Number(e.target.value));
}}
>
{[10, 25, 50].map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
</div>
<div>{table.getPrePaginationRowModel().rows.length} Rows</div>
{/* Debug object, uncomment this to see all the values of the table in realtime */}
{/* <pre>{JSON.stringify(table.getState(), null, 2)}</pre> */}
>
{[10, 25, 50].map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
</div>
<div>{table.getPrePaginationRowModel().rows.length} Rows</div>
{/* Debug object, uncomment this to see all the values of the table in realtime */}
{/* <pre>{JSON.stringify(table.getState(), null, 2)}</pre> */}
</TableContainer>
</div>
);
}

function Filter({ column, table }) {
function Filter({ header, table }) {
const { column, getSize } = header;
const columnFilterValue = column.getFilterValue();
return (
<>
<DebouncedInput
style={{ width: getSize() }}
type="text"
autoComplete="off"
value={columnFilterValue ?? ""}
onChange={(value) => column.setFilterValue(value)}
placeholder={`Search... (${column.getFacetedUniqueValues().size})`}
className="w-36 border shadow rounded"
list={column.id + "list"}
/>
<div className="h-1" />
Expand Down

0 comments on commit 65033d9

Please sign in to comment.