diff --git a/astro-app/src/components/ProjectsTable.tsx b/astro-app/src/components/ProjectsTable.tsx index f12d596..c7d977b 100644 --- a/astro-app/src/components/ProjectsTable.tsx +++ b/astro-app/src/components/ProjectsTable.tsx @@ -1,4 +1,4 @@ -import { useState, useMemo } from 'react'; +import { useState, useMemo, useRef } from 'react'; import { useVirtualizer } from '@tanstack/react-virtual'; import { createColumnHelper, @@ -10,7 +10,7 @@ import { type SortingState, type ColumnFiltersState } from '@tanstack/react-table'; -import { ArrowUpDown, ArrowUp, ArrowDown, Search, Filter, ExternalLink } from 'lucide-react'; +import { ArrowUpDown, ArrowUp, ArrowDown, Search, ExternalLink } from 'lucide-react'; import { formatDistanceToNow } from 'date-fns'; import type { TwitterProject } from '@/lib/csv-loader'; import { Button } from './ui/button'; @@ -29,6 +29,7 @@ export function ProjectsTable({ projects, title, showUrlColumn = true }: Project const [sorting, setSorting] = useState([]); const [columnFilters, setColumnFilters] = useState([]); const [globalFilter, setGlobalFilter] = useState(''); + const parentRef = useRef(null); const columns = useMemo(() => [ columnHelper.accessor('author_name', { @@ -36,7 +37,7 @@ export function ProjectsTable({ projects, title, showUrlColumn = true }: Project ), cell: ({ row }) => ( -
-
+
+
{row.original.author_name.charAt(0).toUpperCase()}
-
-
{row.original.author_name}
-
@{row.original.author_screen_name}
+
+
{row.original.author_name}
+
@{row.original.author_screen_name}
), + size: 200, }), columnHelper.accessor('project_description', { header: ({ column }) => ( ), cell: ({ row }) => ( -
+

{row.original.project_description}

), + size: 350, }), ...(showUrlColumn ? [ columnHelper.accessor('project_url', { header: 'Project', - cell: ({ row }) => - row.original.project_url ? ( - - - View - - ) : ( - No URL - ), + cell: ({ row }) => ( +
+ {row.original.project_url ? ( + + + View + + ) : ( + No URL + )} +
+ ), + size: 100, }) ] : []), columnHelper.accessor('media_thumbnail', { header: 'Media', - cell: ({ row }) => - row.original.media_thumbnail ? ( - Project preview - ) : ( -
- No media -
- ), + cell: ({ row }) => ( +
+ {row.original.media_thumbnail ? ( + Project preview + ) : ( +
+ No media +
+ )} +
+ ), + size: 80, }), columnHelper.accessor('created_at', { header: ({ column }) => ( ), cell: ({ row }) => ( -
+
{formatDistanceToNow(new Date(row.original.created_at), { addSuffix: true })}
), + size: 120, }), columnHelper.accessor('favorite_count', { header: ({ column }) => ( ), cell: ({ row }) => ( -
+
{row.original.favorite_count.toLocaleString()}
), + size: 80, }), columnHelper.accessor('category', { header: ({ column }) => ( ), cell: ({ row }) => ( - - {row.original.category} - +
+ + {row.original.category} + +
), + size: 120, }), ], [showUrlColumn]); @@ -205,10 +221,11 @@ export function ProjectsTable({ projects, title, showUrlColumn = true }: Project }, }); - const parentRef = useState(null); + const { rows } = table.getRowModel(); + const virtualizer = useVirtualizer({ - count: table.getRowModel().rows.length, - getScrollElement: () => parentRef[0], + count: rows.length, + getScrollElement: () => parentRef.current, estimateSize: () => 80, overscan: 5, }); @@ -256,46 +273,58 @@ export function ProjectsTable({ projects, title, showUrlColumn = true }: Project
{/* Table */} -
-
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - ))} - - ))} - - - - - +
+
+ {/* Header */} +
+ {table.getHeaderGroups().map((headerGroup) => ( +
+ {headerGroup.headers.map((header) => ( +
+ {header.isPlaceholder + ? null + : flexRender(header.column.columnDef.header, header.getContext())} +
+ ))} +
+ ))} +
+ + {/* Virtual Scrollable Body */} +
+
{virtualizer.getVirtualItems().map((virtualRow) => { - const row = table.getRowModel().rows[virtualRow.index]; + const row = rows[virtualRow.index]; return ( -
{row.getVisibleCells().map((cell) => ( - + ))} - + ); })} - -
- {header.isPlaceholder - ? null - : flexRender(header.column.columnDef.header, header.getContext())} -
+
{flexRender(cell.column.columnDef.cell, cell.getContext())} -
+
+