Skip to content

Commit 65033d9

Browse files
committed
fix perf issues, added resizable columns
1 parent 820e852 commit 65033d9

1 file changed

Lines changed: 204 additions & 122 deletions

File tree

src/components/DynamicTableView.jsx

Lines changed: 204 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -22,28 +22,81 @@ const columnsWithSubRows = [
2222
"httpRealm",
2323
];
2424

25+
const TableContainer = styled.div`
26+
overflow-x: "auto";
27+
`;
2528
// styled-components allow us to create smaller customizable components
2629
const StyledTable = styled.table`
30+
31+
border: 1px solid #8080804f;
32+
border-radius: 4px;
33+
margin-bottom: 4px;
34+
35+
tr {
36+
width: fit-content;
37+
height: 30px;
38+
}
39+
40+
th {
41+
position: relative;
42+
}
43+
44+
td {
45+
padding: 4px;
46+
height: 30px;
47+
position: relative;
48+
}
49+
2750
tbody tr {
2851
:nth-of-type(odd) {
2952
background-color: #f0f0f0;
3053
}
3154
:hover {
3255
background-color: lightgrey;
3356
}
57+
58+
th {
59+
padding: 2px 4px;
60+
position: relative;
61+
font-weight: bold;
62+
text-align: center;
63+
height: 30px;
64+
}
65+
} // end of tbody
66+
67+
.resizer {
68+
position: absolute;
69+
right: 0;
70+
top: 0;
71+
height: 100%;
72+
width: 5px;
73+
background: rgba(0, 0, 0, 0.5);
74+
cursor: col-resize;
75+
user-select: none;
76+
touch-action: none;
3477
}
3578
36-
td {
37-
padding: 4px;
79+
.resizer.isResizing {
80+
background: blue;
81+
opacity: 1;
3882
}
83+
84+
@media (hover: hover) {
85+
.resizer {
86+
opacity: 0;
87+
}
88+
89+
*:hover > .resizer {
90+
opacity: 1;
91+
}
3992
`;
4093

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

46-
let records = JSON.parse(stringify(props.data));
99+
let records = props.data;
47100

48101
let tableKeys = new Set();
49102
// Since each table comes with it's own schema, we just iterate over
@@ -104,6 +157,7 @@ export default function DynamicTableView(props) {
104157
const table = useReactTable({
105158
data,
106159
columns,
160+
columnResizeMode: "onChange",
107161
state: {
108162
columnFilters,
109163
globalFilter,
@@ -115,8 +169,8 @@ export default function DynamicTableView(props) {
115169
return row.tabs;
116170
}
117171

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

138192
return (
139193
<div className="p-2">
140-
<div className="h-2" />
141-
<StyledTable>
142-
<thead>
143-
{table.getHeaderGroups().map((headerGroup) => (
144-
<tr key={headerGroup.id}>
145-
{headerGroup.headers.map((header) => {
146-
return (
147-
<th key={header.id} colSpan={header.colSpan}>
148-
{header.isPlaceholder ? null : (
149-
<>
150-
<div>
151-
{flexRender(
194+
<TableContainer>
195+
<StyledTable
196+
{...{
197+
style: {
198+
width: table.getCenterTotalSize(),
199+
},
200+
}}
201+
>
202+
<thead>
203+
{table.getHeaderGroups().map((headerGroup) => (
204+
<tr key={headerGroup.id}>
205+
{headerGroup.headers.map((header) => {
206+
return (
207+
<th
208+
{...{
209+
key: header.id,
210+
colSpan: header.colSpan,
211+
style: {
212+
width: header.getSize(),
213+
},
214+
}}
215+
>
216+
{header.isPlaceholder
217+
? null
218+
: flexRender(
152219
header.column.columnDef.header,
153220
header.getContext()
154221
)}
222+
<div
223+
{...{
224+
onMouseDown: header.getResizeHandler(),
225+
onTouchStart: header.getResizeHandler(),
226+
className: `resizer ${
227+
header.column.getIsResizing() ? "isResizing" : ""
228+
}`,
229+
}}
230+
/>
231+
{header.column.getCanFilter() ? (
232+
<div>
233+
<Filter header={header} table={table} />
155234
</div>
156-
{header.column.getCanFilter() ? (
157-
<div>
158-
<Filter column={header.column} table={table} />
159-
</div>
160-
) : null}
161-
</>
162-
)}
163-
</th>
164-
);
165-
})}
166-
</tr>
167-
))}
168-
</thead>
169-
<tbody>
170-
{table.getRowModel().rows.map((row) => {
171-
return (
172-
<Fragment key={row.id}>
173-
<tr>
174-
{row.getVisibleCells().map((cell) => {
175-
return (
176-
<td key={cell.id}>
177-
{flexRender(
178-
cell.column.columnDef.cell,
179-
cell.getContext()
180-
)}
181-
</td>
182-
);
183-
})}
184-
</tr>
185-
{row.getIsExpanded() && (
235+
) : null}
236+
</th>
237+
);
238+
})}
239+
</tr>
240+
))}
241+
</thead>
242+
<tbody>
243+
{table.getRowModel().rows.map((row) => {
244+
return (
245+
<Fragment key={row.id}>
186246
<tr>
187-
{/* 2nd row is a custom 1 cell row */}
188-
<td colSpan={row.getVisibleCells().length}>
189-
{renderSubComponent({ row })}
190-
</td>
247+
{row.getVisibleCells().map((cell) => {
248+
return (
249+
<td
250+
{...{
251+
key: cell.id,
252+
style: {
253+
width: cell.column.getSize(),
254+
},
255+
}}
256+
>
257+
{flexRender(
258+
cell.column.columnDef.cell,
259+
cell.getContext()
260+
)}
261+
</td>
262+
);
263+
})}
191264
</tr>
192-
)}
193-
</Fragment>
194-
);
195-
})}
196-
</tbody>
197-
</StyledTable>
198-
<div className="h-2" />
199-
<div className="flex items-center gap-2">
200-
<button
201-
className="border rounded p-1"
202-
onClick={() => table.setPageIndex(0)}
203-
disabled={!table.getCanPreviousPage()}
204-
>
205-
{"<<"}
206-
</button>
207-
<button
208-
className="border rounded p-1"
209-
onClick={() => table.previousPage()}
210-
disabled={!table.getCanPreviousPage()}
211-
>
212-
{"<"}
213-
</button>
214-
<button
215-
className="border rounded p-1"
216-
onClick={() => table.nextPage()}
217-
disabled={!table.getCanNextPage()}
218-
>
219-
{">"}
220-
</button>
221-
<button
222-
className="border rounded p-1"
223-
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
224-
disabled={!table.getCanNextPage()}
225-
>
226-
{">>"}
227-
</button>
228-
<span className="flex items-center gap-1">
229-
<div>Page</div>
230-
<strong>
231-
{table.getState().pagination.pageIndex + 1} of{" "}
232-
{table.getPageCount()}
233-
</strong>
234-
</span>
235-
<span className="flex items-center gap-1">
236-
| Go to page:
237-
<input
238-
type="number"
239-
defaultValue={table.getState().pagination.pageIndex + 1}
265+
{row.getIsExpanded() && (
266+
<tr>
267+
{/* 2nd row is a custom 1 cell row */}
268+
<td colSpan={row.getVisibleCells().length}>
269+
{renderSubComponent({ row })}
270+
</td>
271+
</tr>
272+
)}
273+
</Fragment>
274+
);
275+
})}
276+
</tbody>
277+
</StyledTable>
278+
<div className="h-2" />
279+
<div className="flex items-center gap-2">
280+
<button
281+
className="border rounded p-1"
282+
onClick={() => table.setPageIndex(0)}
283+
disabled={!table.getCanPreviousPage()}
284+
>
285+
{"<<"}
286+
</button>
287+
<button
288+
className="border rounded p-1"
289+
onClick={() => table.previousPage()}
290+
disabled={!table.getCanPreviousPage()}
291+
>
292+
{"<"}
293+
</button>
294+
<button
295+
className="border rounded p-1"
296+
onClick={() => table.nextPage()}
297+
disabled={!table.getCanNextPage()}
298+
>
299+
{">"}
300+
</button>
301+
<button
302+
className="border rounded p-1"
303+
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
304+
disabled={!table.getCanNextPage()}
305+
>
306+
{">>"}
307+
</button>
308+
<span className="flex items-center gap-1">
309+
<div>Page</div>
310+
<strong>
311+
{table.getState().pagination.pageIndex + 1} of{" "}
312+
{table.getPageCount()}
313+
</strong>
314+
</span>
315+
<span className="flex items-center gap-1">
316+
| Go to page:
317+
<input
318+
type="number"
319+
defaultValue={table.getState().pagination.pageIndex + 1}
320+
onChange={(e) => {
321+
const page = e.target.value ? Number(e.target.value) - 1 : 0;
322+
table.setPageIndex(page);
323+
}}
324+
className="border p-1 rounded w-16"
325+
/>
326+
</span>
327+
<select
328+
value={table.getState().pagination.pageSize}
240329
onChange={(e) => {
241-
const page = e.target.value ? Number(e.target.value) - 1 : 0;
242-
table.setPageIndex(page);
330+
table.setPageSize(Number(e.target.value));
243331
}}
244-
className="border p-1 rounded w-16"
245-
/>
246-
</span>
247-
<select
248-
value={table.getState().pagination.pageSize}
249-
onChange={(e) => {
250-
table.setPageSize(Number(e.target.value));
251-
}}
252-
>
253-
{[10, 25, 50].map((pageSize) => (
254-
<option key={pageSize} value={pageSize}>
255-
Show {pageSize}
256-
</option>
257-
))}
258-
</select>
259-
</div>
260-
<div>{table.getPrePaginationRowModel().rows.length} Rows</div>
261-
{/* Debug object, uncomment this to see all the values of the table in realtime */}
262-
{/* <pre>{JSON.stringify(table.getState(), null, 2)}</pre> */}
332+
>
333+
{[10, 25, 50].map((pageSize) => (
334+
<option key={pageSize} value={pageSize}>
335+
Show {pageSize}
336+
</option>
337+
))}
338+
</select>
339+
</div>
340+
<div>{table.getPrePaginationRowModel().rows.length} Rows</div>
341+
{/* Debug object, uncomment this to see all the values of the table in realtime */}
342+
{/* <pre>{JSON.stringify(table.getState(), null, 2)}</pre> */}
343+
</TableContainer>
263344
</div>
264345
);
265346
}
266347

267-
function Filter({ column, table }) {
348+
function Filter({ header, table }) {
349+
const { column, getSize } = header;
268350
const columnFilterValue = column.getFilterValue();
269351
return (
270352
<>
271353
<DebouncedInput
354+
style={{ width: getSize() }}
272355
type="text"
273356
autoComplete="off"
274357
value={columnFilterValue ?? ""}
275358
onChange={(value) => column.setFilterValue(value)}
276359
placeholder={`Search... (${column.getFacetedUniqueValues().size})`}
277-
className="w-36 border shadow rounded"
278360
list={column.id + "list"}
279361
/>
280362
<div className="h-1" />

0 commit comments

Comments
 (0)