mirror of https://github.com/buster-so/buster.git
Update AppDataGrid2.tsx
This commit is contained in:
parent
8205e72f3f
commit
4f50e908b4
|
@ -1,9 +1,10 @@
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
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> = {
|
const meta: Meta<typeof TanStackDataGrid> = {
|
||||||
title: 'UI/Table/AppDataGrid2',
|
title: 'UI/Table/TanStackDataGrid',
|
||||||
component: AppDataGrid2,
|
component: TanStackDataGrid,
|
||||||
parameters: {
|
parameters: {
|
||||||
layout: 'fullscreen'
|
layout: 'fullscreen'
|
||||||
},
|
},
|
||||||
|
@ -11,52 +12,15 @@ const meta: Meta<typeof AppDataGrid2> = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default meta;
|
export default meta;
|
||||||
type Story = StoryObj<typeof AppDataGrid2>;
|
type Story = StoryObj<typeof TanStackDataGrid>;
|
||||||
|
|
||||||
const sampleData = [
|
const sampleData = Array.from({ length: 1000 }, (_, index) => ({
|
||||||
{
|
id: index + 1,
|
||||||
id: 1,
|
name: faker.person.fullName(),
|
||||||
name: 'John Doe',
|
age: faker.number.int({ min: 18, max: 90 }),
|
||||||
age: 30,
|
email: faker.internet.email(),
|
||||||
email: 'john@example.com',
|
joinDate: faker.date.past().toISOString()
|
||||||
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()
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export const Default: Story = {
|
export const Default: Story = {
|
||||||
args: {
|
args: {
|
||||||
|
@ -66,8 +30,8 @@ export const Default: Story = {
|
||||||
sortable: true
|
sortable: true
|
||||||
},
|
},
|
||||||
render: (args) => (
|
render: (args) => (
|
||||||
<div className="h-[500px] overflow-y-auto border p-3">
|
<div className="h-[500px] p-0">
|
||||||
<AppDataGrid2 {...args} />
|
<TanStackDataGrid {...args} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
};
|
};
|
|
@ -16,7 +16,6 @@ import {
|
||||||
KeyboardSensor,
|
KeyboardSensor,
|
||||||
useSensor,
|
useSensor,
|
||||||
useSensors,
|
useSensors,
|
||||||
closestCenter,
|
|
||||||
pointerWithin,
|
pointerWithin,
|
||||||
DragEndEvent,
|
DragEndEvent,
|
||||||
DragOverlay,
|
DragOverlay,
|
||||||
|
@ -27,13 +26,13 @@ import {
|
||||||
} from '@dnd-kit/core';
|
} from '@dnd-kit/core';
|
||||||
import { arrayMove } from '@dnd-kit/sortable';
|
import { arrayMove } from '@dnd-kit/sortable';
|
||||||
import sampleSize from 'lodash/sampleSize';
|
import sampleSize from 'lodash/sampleSize';
|
||||||
import { defaultCellFormat, defaultHeaderFormat } from './helpers';
|
import { defaultCellFormat, defaultHeaderFormat } from '../helpers';
|
||||||
import { cn } from '@/lib/classMerge';
|
import { cn } from '@/lib/classMerge';
|
||||||
import { restrictToHorizontalAxis } from '@dnd-kit/modifiers';
|
import { restrictToHorizontalAxis } from '@dnd-kit/modifiers';
|
||||||
import { useMemoizedFn } from '@/hooks';
|
import { useMemoizedFn } from '@/hooks';
|
||||||
import { CaretDown, CaretUp } from '../../icons/NucleoIconFilled';
|
import { CaretDown, CaretUp } from '../../../icons/NucleoIconFilled';
|
||||||
|
|
||||||
export interface AppDataGridProps {
|
export interface TanStackDataGridProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
resizable?: boolean;
|
resizable?: boolean;
|
||||||
sortable?: boolean;
|
sortable?: boolean;
|
||||||
|
@ -95,22 +94,23 @@ const DraggableHeader: React.FC<DraggableHeaderProps> = React.memo(
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<th
|
||||||
ref={setDropNodeRef}
|
ref={setDropNodeRef}
|
||||||
style={style}
|
style={style}
|
||||||
className={cn(
|
className={cn(
|
||||||
'bg-background relative border select-none',
|
'relative border-r select-none last:border-r-0',
|
||||||
isOverTarget && 'bg-primary/10 border-primary rounded-sm border-dashed'
|
isOverTarget &&
|
||||||
|
'bg-primary/10 border-primary inset rounded-sm border border-r border-dashed'
|
||||||
)}
|
)}
|
||||||
// onClick toggles sorting if enabled
|
// onClick toggles sorting if enabled
|
||||||
onClick={header.column.getCanSort() ? header.column.getToggleSortingHandler() : undefined}>
|
onClick={header.column.getCanSort() ? header.column.getToggleSortingHandler() : undefined}>
|
||||||
<div
|
<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}
|
ref={sortable ? setDragNodeRef : undefined}
|
||||||
{...attributes}
|
{...attributes}
|
||||||
{...listeners}
|
{...listeners}
|
||||||
style={{ cursor: 'grab' }}>
|
style={{ cursor: 'grab' }}>
|
||||||
<span className="text-gray-dark">
|
<span className="text-gray-dark text-base font-normal">
|
||||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||||
</span>
|
</span>
|
||||||
{header.column.getIsSorted() === 'asc' && (
|
{header.column.getIsSorted() === 'asc' && (
|
||||||
|
@ -140,7 +140,7 @@ const DraggableHeader: React.FC<DraggableHeaderProps> = React.memo(
|
||||||
/>
|
/>
|
||||||
</div>
|
</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 = '',
|
className = '',
|
||||||
resizable = true,
|
resizable = true,
|
||||||
|
@ -355,28 +355,15 @@ export const AppDataGrid2: React.FC<AppDataGridProps> = React.memo(
|
||||||
const rowVirtualizer = useVirtualizer({
|
const rowVirtualizer = useVirtualizer({
|
||||||
count: table.getRowModel().rows.length,
|
count: table.getRowModel().rows.length,
|
||||||
getScrollElement: () => parentRef.current,
|
getScrollElement: () => parentRef.current,
|
||||||
estimateSize: () => 35, // estimated row height
|
estimateSize: () => 36, // estimated row height
|
||||||
overscan: 5
|
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 (
|
return (
|
||||||
<div ref={parentRef} className={cn('h-full w-full overflow-auto', className)}>
|
<div ref={parentRef} className={cn('h-full w-full overflow-auto', className)}>
|
||||||
|
<table className="bg-background w-full">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="sticky top-0 z-10 w-full bg-gray-100" ref={headerRef}>
|
<thead className="bg-background sticky top-0 z-10 w-full">
|
||||||
<DndContext
|
<DndContext
|
||||||
sensors={sensors}
|
sensors={sensors}
|
||||||
modifiers={[restrictToHorizontalAxis]}
|
modifiers={[restrictToHorizontalAxis]}
|
||||||
|
@ -384,19 +371,18 @@ export const AppDataGrid2: React.FC<AppDataGridProps> = React.memo(
|
||||||
onDragStart={handleDragStart}
|
onDragStart={handleDragStart}
|
||||||
onDragOver={handleDragOver}
|
onDragOver={handleDragOver}
|
||||||
onDragEnd={handleDragEnd}>
|
onDragEnd={handleDragEnd}>
|
||||||
<div className="flex">
|
<tr className="flex border-b">
|
||||||
{table
|
{table
|
||||||
.getHeaderGroups()[0]
|
.getHeaderGroups()[0]
|
||||||
?.headers.map((header) => (
|
?.headers.map((header) => (
|
||||||
<DraggableHeader
|
<DraggableHeader
|
||||||
key={header.id}
|
|
||||||
header={header}
|
header={header}
|
||||||
sortable={sortable}
|
sortable={sortable}
|
||||||
resizable={resizable}
|
resizable={resizable}
|
||||||
overTargetId={overTargetId}
|
overTargetId={overTargetId}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</tr>
|
||||||
|
|
||||||
{/* Drag Overlay */}
|
{/* Drag Overlay */}
|
||||||
<DragOverlay
|
<DragOverlay
|
||||||
|
@ -406,31 +392,38 @@ export const AppDataGrid2: React.FC<AppDataGridProps> = React.memo(
|
||||||
{activeId && activeHeader && <HeaderDragOverlay header={activeHeader} />}
|
{activeId && activeHeader && <HeaderDragOverlay header={activeHeader} />}
|
||||||
</DragOverlay>
|
</DragOverlay>
|
||||||
</DndContext>
|
</DndContext>
|
||||||
</div>
|
</thead>
|
||||||
|
|
||||||
{/* Body */}
|
{/* Body */}
|
||||||
<div className="relative" style={{ height: `${rowVirtualizer.getTotalSize()}px` }}>
|
<tbody
|
||||||
|
className="relative"
|
||||||
|
style={{ display: 'grid', height: `${rowVirtualizer.getTotalSize()}px` }}>
|
||||||
{rowVirtualizer.getVirtualItems().map((virtualRow) => {
|
{rowVirtualizer.getVirtualItems().map((virtualRow) => {
|
||||||
const row = table.getRowModel().rows[virtualRow.index];
|
const row = table.getRowModel().rows[virtualRow.index];
|
||||||
return (
|
return (
|
||||||
<div
|
<tr
|
||||||
key={row.id}
|
key={row.id}
|
||||||
className="absolute inset-x-0 flex"
|
className="absolute inset-x-0 flex border-b last:border-b-0"
|
||||||
style={{
|
style={{
|
||||||
transform: `translateY(${virtualRow.start}px)`,
|
transform: `translateY(${virtualRow.start}px)`,
|
||||||
height: `${virtualRow.size}px`
|
height: `${virtualRow.size}px`
|
||||||
}}>
|
}}>
|
||||||
{row.getVisibleCells().map((cell) => (
|
{row.getVisibleCells().map((cell) => (
|
||||||
<td key={cell.id} className="border p-2" style={{ width: cell.column.getSize() }}>
|
<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())}
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
</td>
|
</td>
|
||||||
))}
|
))}
|
||||||
</div>
|
</tr>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
AppDataGrid2.displayName = 'AppDataGrid2';
|
TanStackDataGrid.displayName = 'TanStackDataGrid';
|
Loading…
Reference in New Issue