Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Repository table CSS tweaks #106

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
8 changes: 5 additions & 3 deletions packages/web/src/app/repos/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const columns: ColumnDef<RepositoryColumnInfo>[] = [
{
accessorKey: "name",
header: "Name",
size: 250,
cell: ({ row }) => {
const repo = row.original;
const url = repo.url;
Expand All @@ -33,7 +34,7 @@ export const columns: ColumnDef<RepositoryColumnInfo>[] = [
return (
<div className="flex flex-row items-center gap-2">
<span
className={!isRemoteRepo ? "cursor-pointer text-blue-500 hover:underline": ""}
className={"whitespace-normal break-all " + (!isRemoteRepo ? "cursor-pointer text-blue-500 hover:underline" : "")}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

micro nit: usually when adding conditional CSS, we use clsx

onClick={() => {
if (!isRemoteRepo) {
window.open(url, "_blank");
Expand Down Expand Up @@ -61,7 +62,8 @@ export const columns: ColumnDef<RepositoryColumnInfo>[] = [
{branches.map(({ name, version }, index) => {
const shortVersion = version.substring(0, 8);
return (
<span key={index}>
<span
key={index}>
{name}
@
<span
Expand Down Expand Up @@ -138,4 +140,4 @@ const createSortHeader = (name: string, column: Column<RepositoryColumnInfo, unk
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
)
}
}
4 changes: 2 additions & 2 deletions packages/web/src/app/repos/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ export default function ReposPage() {
<div className="h-screen flex flex-col items-center">
<NavigationMenu />
<Suspense fallback={<div>Loading...</div>}>
<div className="max-w-[90%]">
<div className="h-fill max-w-[90%]">
<RepositoryTable />
</div>
</Suspense>
</div>
)
}
}
7 changes: 3 additions & 4 deletions packages/web/src/app/repos/repositoryTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,14 @@ export const RepositoryTable = async () => {
url: repo.Repository.URL,
}
}).sort((a, b) => {
return new Date(b.lastIndexed).getTime() - new Date(a.lastIndexed).getTime();
return new Date(b.lastIndexed).getTime() - new Date(a.lastIndexed).getTime();
});

return (
<DataTable
columns={columns}
data={repos}
searchKey="name"
searchPlaceholder="Search repositories..."
searchPlaceholder={`Search ${repos.length} repositories...`}
/>
);
}
}
178 changes: 107 additions & 71 deletions packages/web/src/components/ui/data-table.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client"
"use client";

import React from "react";
import {
ColumnDef,
ColumnFiltersState,
Expand All @@ -10,25 +11,24 @@ import {
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table"
} from "@tanstack/react-table";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import * as React from "react"

DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
} from "@/components/ui/dropdown-menu";
import { ChevronDown } from "lucide-react";

interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
data: TData[]
searchKey: string
searchPlaceholder?: string
columns: ColumnDef<TData, TValue>[];
data: TData[];
searchKey: string;
searchPlaceholder?: string;
}

export function DataTable<TData, TValue>({
Expand All @@ -37,100 +37,136 @@ export function DataTable<TData, TValue>({
searchKey,
searchPlaceholder,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = React.useState<SortingState>([])
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
)
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
const [pagination, setPagination] = React.useState({
pageIndex: 0,
pageSize: 10, // Default page size
});

const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
onSortingChange: setSorting,
getSortedRowModel: getSortedRowModel(),
onColumnFiltersChange: setColumnFilters,
getFilteredRowModel: getFilteredRowModel(),
state: {
sorting,
columnFilters,
pagination,
},
})
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
onPaginationChange: setPagination,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
});

return (
<div>
<div className="flex items-center py-4">
<div className="pb-12">
<div className="flex items-center justify-between py-4">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could we add a optional className props to DataTableProps, and then have RepositoryTable pass down this class?

<Input
placeholder={searchPlaceholder}
value={(table.getColumn(searchKey)?.getFilterValue() as string) ?? ""}
onChange={(event) =>
table.getColumn(searchKey)?.setFilterValue(event.target.value)
}
onChange={(event) => table.getColumn(searchKey)?.setFilterValue(event.target.value)}
className="max-w-sm"
/>

{/* Pagination Controls */}
<div className="flex items-center space-x-4">
<span className="text-sm font-medium min-w-20">
Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount()}
</span>
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>

{/* Radix Dropdown for items per page */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
{/* Fixed width here to prevent layout shift */}
<Button variant="outline" size="sm" className="w-28 justify-between">
Show {pagination.pageSize}
<ChevronDown className="ml-2 h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-28">
<DropdownMenuRadioGroup
value={String(pagination.pageSize)}
onValueChange={(value) =>
setPagination((prev) => ({
...prev,
pageSize: Number(value),
}))
}
>
<DropdownMenuRadioItem value="10">10</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="20">20</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="50">50</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="100">100</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>

<div className="rounded-md border">
<Table>
<Table className="table-fixed">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
)
})}
{headerGroup.headers.map((header) => (
<TableHead
key={header.id}
style={{ width: header.column.getSize() }}
>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
<TableRow key={row.id} data-state={row.getIsSelected() && "selected"}>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
<TableCell key={cell.id} style={{ width: cell.column.getSize() }}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
)
);
}
4 changes: 2 additions & 2 deletions packages/web/src/components/ui/dropdown-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const DropdownMenuSubContent = React.forwardRef<
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
"z-50 min-w-[6rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of modifying the component directly here, could we pass the min-w-[6rem] in the className property of the DropdownMenu?

className
)}
{...props}
Expand All @@ -65,7 +65,7 @@ const DropdownMenuContent = React.forwardRef<
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
"z-50 min-w-[6rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
Expand Down
Loading