Update AppDataGrid2.tsx

This commit is contained in:
Nate Kelley 2025-04-04 14:47:15 -06:00
parent 8205e72f3f
commit 4f50e908b4
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
2 changed files with 87 additions and 130 deletions

View File

@ -1,9 +1,10 @@
import type { Meta, StoryObj } from '@storybook/react';
import { AppDataGrid2 } from './AppDataGrid2';
import { TanStackDataGrid } from './TanStackDataGrid';
import { faker } from '@faker-js/faker';
const meta: Meta<typeof AppDataGrid2> = {
title: 'UI/Table/AppDataGrid2',
component: AppDataGrid2,
const meta: Meta<typeof TanStackDataGrid> = {
title: 'UI/Table/TanStackDataGrid',
component: TanStackDataGrid,
parameters: {
layout: 'fullscreen'
},
@ -11,52 +12,15 @@ const meta: Meta<typeof AppDataGrid2> = {
};
export default meta;
type Story = StoryObj<typeof AppDataGrid2>;
type Story = StoryObj<typeof TanStackDataGrid>;
const sampleData = [
{
id: 1,
name: 'John Doe',
age: 30,
email: 'john@example.com',
joinDate: new Date('2023-01-15').toISOString()
},
{
id: 2,
name: 'Jane Smith',
age: 25,
email: 'jane@example.com',
joinDate: new Date('2023-02-20').toISOString()
},
{
id: 3,
name: 'Bob Johnson',
age: 35,
email: 'bob@example.com',
joinDate: new Date('2023-03-10').toISOString()
},
{
id: 4,
name: 'Alice Brown',
age: 28,
email: 'alice@example.com',
joinDate: new Date('2023-04-05').toISOString()
},
{
id: 5,
name: 'Michael Wilson',
age: 42,
email: 'michael@example.com',
joinDate: new Date('2023-05-12').toISOString()
},
{
id: 6,
name: 'Sarah Davis',
age: 31,
email: 'sarah@example.com',
joinDate: new Date('2023-06-08').toISOString()
}
];
const sampleData = Array.from({ length: 1000 }, (_, index) => ({
id: index + 1,
name: faker.person.fullName(),
age: faker.number.int({ min: 18, max: 90 }),
email: faker.internet.email(),
joinDate: faker.date.past().toISOString()
}));
export const Default: Story = {
args: {
@ -66,8 +30,8 @@ export const Default: Story = {
sortable: true
},
render: (args) => (
<div className="h-[500px] overflow-y-auto border p-3">
<AppDataGrid2 {...args} />
<div className="h-[500px] p-0">
<TanStackDataGrid {...args} />
</div>
)
};

View File

@ -16,7 +16,6 @@ import {
KeyboardSensor,
useSensor,
useSensors,
closestCenter,
pointerWithin,
DragEndEvent,
DragOverlay,
@ -27,13 +26,13 @@ import {
} from '@dnd-kit/core';
import { arrayMove } from '@dnd-kit/sortable';
import sampleSize from 'lodash/sampleSize';
import { defaultCellFormat, defaultHeaderFormat } from './helpers';
import { defaultCellFormat, defaultHeaderFormat } from '../helpers';
import { cn } from '@/lib/classMerge';
import { restrictToHorizontalAxis } from '@dnd-kit/modifiers';
import { useMemoizedFn } from '@/hooks';
import { CaretDown, CaretUp } from '../../icons/NucleoIconFilled';
import { CaretDown, CaretUp } from '../../../icons/NucleoIconFilled';
export interface AppDataGridProps {
export interface TanStackDataGridProps {
className?: string;
resizable?: boolean;
sortable?: boolean;
@ -95,22 +94,23 @@ const DraggableHeader: React.FC<DraggableHeaderProps> = React.memo(
};
return (
<div
<th
ref={setDropNodeRef}
style={style}
className={cn(
'bg-background relative border select-none',
isOverTarget && 'bg-primary/10 border-primary rounded-sm border-dashed'
'relative border-r select-none last:border-r-0',
isOverTarget &&
'bg-primary/10 border-primary inset rounded-sm border border-r border-dashed'
)}
// onClick toggles sorting if enabled
onClick={header.column.getCanSort() ? header.column.getToggleSortingHandler() : undefined}>
<div
className="flex h-full flex-1 items-center space-x-1 p-2"
className="flex h-full flex-1 items-center space-x-1.5 p-2"
ref={sortable ? setDragNodeRef : undefined}
{...attributes}
{...listeners}
style={{ cursor: 'grab' }}>
<span className="text-gray-dark">
<span className="text-gray-dark text-base font-normal">
{flexRender(header.column.columnDef.header, header.getContext())}
</span>
{header.column.getIsSorted() === 'asc' && (
@ -140,7 +140,7 @@ const DraggableHeader: React.FC<DraggableHeaderProps> = React.memo(
/>
</div>
)}
</div>
</th>
);
}
);
@ -170,7 +170,7 @@ const HeaderDragOverlay = ({
);
};
export const AppDataGrid2: React.FC<AppDataGridProps> = React.memo(
export const TanStackDataGrid: React.FC<TanStackDataGridProps> = React.memo(
({
className = '',
resizable = true,
@ -355,82 +355,75 @@ export const AppDataGrid2: React.FC<AppDataGridProps> = React.memo(
const rowVirtualizer = useVirtualizer({
count: table.getRowModel().rows.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 35, // estimated row height
estimateSize: () => 36, // estimated row height
overscan: 5
});
// Create a reference to measure header height
const headerRef = useRef<HTMLDivElement>(null);
const [headerHeight, setHeaderHeight] = useState(36); // Default height
// Measure the actual header height once mounted
useEffect(() => {
if (headerRef.current) {
const height = headerRef.current.getBoundingClientRect().height;
if (height > 0) {
setHeaderHeight(height);
}
}
}, []);
return (
<div ref={parentRef} className={cn('h-full w-full overflow-auto', className)}>
{/* Header */}
<div className="sticky top-0 z-10 w-full bg-gray-100" ref={headerRef}>
<DndContext
sensors={sensors}
modifiers={[restrictToHorizontalAxis]}
collisionDetection={pointerWithin}
onDragStart={handleDragStart}
onDragOver={handleDragOver}
onDragEnd={handleDragEnd}>
<div className="flex">
{table
.getHeaderGroups()[0]
?.headers.map((header) => (
<DraggableHeader
key={header.id}
header={header}
sortable={sortable}
resizable={resizable}
overTargetId={overTargetId}
/>
))}
</div>
<table className="bg-background w-full">
{/* Header */}
<thead className="bg-background sticky top-0 z-10 w-full">
<DndContext
sensors={sensors}
modifiers={[restrictToHorizontalAxis]}
collisionDetection={pointerWithin}
onDragStart={handleDragStart}
onDragOver={handleDragOver}
onDragEnd={handleDragEnd}>
<tr className="flex border-b">
{table
.getHeaderGroups()[0]
?.headers.map((header) => (
<DraggableHeader
header={header}
sortable={sortable}
resizable={resizable}
overTargetId={overTargetId}
/>
))}
</tr>
{/* Drag Overlay */}
<DragOverlay
adjustScale={false}
dropAnimation={null} // Using null to completely disable animation
zIndex={1000}>
{activeId && activeHeader && <HeaderDragOverlay header={activeHeader} />}
</DragOverlay>
</DndContext>
</div>
{/* Body */}
<div className="relative" style={{ height: `${rowVirtualizer.getTotalSize()}px` }}>
{rowVirtualizer.getVirtualItems().map((virtualRow) => {
const row = table.getRowModel().rows[virtualRow.index];
return (
<div
key={row.id}
className="absolute inset-x-0 flex"
style={{
transform: `translateY(${virtualRow.start}px)`,
height: `${virtualRow.size}px`
}}>
{row.getVisibleCells().map((cell) => (
<td key={cell.id} className="border p-2" style={{ width: cell.column.getSize() }}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</div>
);
})}
</div>
{/* Drag Overlay */}
<DragOverlay
adjustScale={false}
dropAnimation={null} // Using null to completely disable animation
zIndex={1000}>
{activeId && activeHeader && <HeaderDragOverlay header={activeHeader} />}
</DragOverlay>
</DndContext>
</thead>
{/* Body */}
<tbody
className="relative"
style={{ display: 'grid', height: `${rowVirtualizer.getTotalSize()}px` }}>
{rowVirtualizer.getVirtualItems().map((virtualRow) => {
const row = table.getRowModel().rows[virtualRow.index];
return (
<tr
key={row.id}
className="absolute inset-x-0 flex border-b last:border-b-0"
style={{
transform: `translateY(${virtualRow.start}px)`,
height: `${virtualRow.size}px`
}}>
{row.getVisibleCells().map((cell) => (
<td
key={cell.id}
className="border-r p-2 last:border-r-0"
style={{ width: cell.column.getSize() }}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
);
})}
</tbody>
</table>
</div>
);
}
);
AppDataGrid2.displayName = 'AppDataGrid2';
TanStackDataGrid.displayName = 'TanStackDataGrid';