feat: add virtualized data table resizer
This commit is contained in:
parent
ad18666851
commit
f1aaa7040e
@ -69,6 +69,7 @@ export function VirtualizedInfiniteDataTable<TData>(
|
|||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: flatData,
|
data: flatData,
|
||||||
columns,
|
columns,
|
||||||
|
columnResizeMode: 'onChange',
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -87,13 +88,32 @@ export function VirtualizedInfiniteDataTable<TData>(
|
|||||||
overscan: 5,
|
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) {
|
if (isLoading) {
|
||||||
return <div>Loading...</div>;
|
return <div>Loading...</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="relative h-full overflow-auto"
|
className="virtualized-infinite-data-table relative h-full overflow-auto"
|
||||||
onScroll={(e) => fetchMoreOnBottomReached(e.target as HTMLDivElement)}
|
onScroll={(e) => fetchMoreOnBottomReached(e.target as HTMLDivElement)}
|
||||||
ref={tableContainerRef}
|
ref={tableContainerRef}
|
||||||
>
|
>
|
||||||
@ -101,6 +121,7 @@ export function VirtualizedInfiniteDataTable<TData>(
|
|||||||
<table style={{ display: 'grid' }}>
|
<table style={{ display: 'grid' }}>
|
||||||
<TableHeader
|
<TableHeader
|
||||||
style={{
|
style={{
|
||||||
|
...columnSizeVars,
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
position: 'sticky',
|
position: 'sticky',
|
||||||
top: 0,
|
top: 0,
|
||||||
@ -116,8 +137,9 @@ export function VirtualizedInfiniteDataTable<TData>(
|
|||||||
return (
|
return (
|
||||||
<TableHead
|
<TableHead
|
||||||
key={header.id}
|
key={header.id}
|
||||||
|
className="relative pt-2.5"
|
||||||
style={{
|
style={{
|
||||||
width: header.getSize(),
|
width: `calc(var(--header-${header?.id}-size) * 1px)`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{header.isPlaceholder
|
{header.isPlaceholder
|
||||||
@ -126,6 +148,17 @@ export function VirtualizedInfiniteDataTable<TData>(
|
|||||||
header.column.columnDef.header,
|
header.column.columnDef.header,
|
||||||
header.getContext()
|
header.getContext()
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
|
{...{
|
||||||
|
onDoubleClick: () => header.column.resetSize(),
|
||||||
|
onMouseDown: header.getResizeHandler(),
|
||||||
|
onTouchStart: header.getResizeHandler(),
|
||||||
|
className: `resizer ${
|
||||||
|
header.column.getIsResizing() ? 'isResizing' : ''
|
||||||
|
}`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import './index.css';
|
import './index.css';
|
||||||
|
import './styles/global.less';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
|
28
src/client/styles/global.less
Normal file
28
src/client/styles/global.less
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user