Skip to content

Commit

Permalink
feat: add virtualized data table resizer
Browse files Browse the repository at this point in the history
  • Loading branch information
moonrailgun committed Aug 7, 2024
1 parent ad18666 commit f1aaa70
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 2 deletions.
37 changes: 35 additions & 2 deletions src/client/components/VirtualizedInfiniteDataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export function VirtualizedInfiniteDataTable<TData>(
const table = useReactTable({
data: flatData,
columns,
columnResizeMode: 'onChange',
getCoreRowModel: getCoreRowModel(),
});

Expand All @@ -87,20 +88,40 @@ export function VirtualizedInfiniteDataTable<TData>(
overscan: 5,
});

/**
* Instead of calling `column.getSize()` on every render for every header
* and especially every data cell (very expensive),
* we will calculate all column sizes at once at the root table level in a useMemo
* and pass the column sizes down as CSS variables to the <table> element.
*/
const columnSizeVars = React.useMemo(() => {
const headers = table.getFlatHeaders();
const colSizes: { [key: string]: number } = {};
for (let i = 0; i < headers.length; i++) {
const header = headers[i]!;
colSizes[`--header-${header.id}-size`] = header.getSize();
colSizes[`--col-${header.column.id}-size`] = header.column.getSize();
}
return colSizes;
}, [table.getState().columnSizingInfo, table.getState().columnSizing]);

console.log('columnSizeVars', columnSizeVars);

if (isLoading) {
return <div>Loading...</div>;
}

return (
<div
className="relative h-full overflow-auto"
className="virtualized-infinite-data-table relative h-full overflow-auto"
onScroll={(e) => fetchMoreOnBottomReached(e.target as HTMLDivElement)}
ref={tableContainerRef}
>
{/* Even though we're still using sematic table tags, we must use CSS grid and flexbox for dynamic row heights */}
<table style={{ display: 'grid' }}>
<TableHeader
style={{
...columnSizeVars,
display: 'grid',
position: 'sticky',
top: 0,
Expand All @@ -116,8 +137,9 @@ export function VirtualizedInfiniteDataTable<TData>(
return (
<TableHead
key={header.id}
className="relative pt-2.5"
style={{
width: header.getSize(),
width: `calc(var(--header-${header?.id}-size) * 1px)`,
}}
>
{header.isPlaceholder
Expand All @@ -126,6 +148,17 @@ export function VirtualizedInfiniteDataTable<TData>(
header.column.columnDef.header,
header.getContext()
)}

<div
{...{
onDoubleClick: () => header.column.resetSize(),
onMouseDown: header.getResizeHandler(),
onTouchStart: header.getResizeHandler(),
className: `resizer ${
header.column.getIsResizing() ? 'isResizing' : ''
}`,
}}
/>
</TableHead>
);
})}
Expand Down
1 change: 1 addition & 0 deletions src/client/main.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import './index.css';
import './styles/global.less';

import React from 'react';
import ReactDOM from 'react-dom/client';
Expand Down
28 changes: 28 additions & 0 deletions src/client/styles/global.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.virtualized-infinite-data-table {
.resizer {
position: absolute;
top: 0;
height: 100%;
right: 0;
width: 2px;
cursor: col-resize;
user-select: none;
touch-action: none;

@apply dark:bg-muted bg-neutral-300 transition-all;
}

.resizer.isResizing {
@apply opacity-100 dark:bg-muted bg-neutral-300;
}

@media (hover: hover) {
.resizer {
opacity: 0;
}

*:hover > .resizer {
opacity: 1;
}
}
}

0 comments on commit f1aaa70

Please sign in to comment.