fix: data table fixes (#17141)
* tidy up css * sticky header * add sticky top and left on columns * use group for cell hover when overflowing * strickly type meta as override isnt working * fix: type err --------- Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com> Co-authored-by: Udit Takkar <udit222001@gmail.com>
This commit is contained in:
co-authored by
Udit Takkar
Udit Takkar
parent
55f1019908
commit
73c35ba5a2
@@ -9,6 +9,7 @@ import {
|
||||
useReactTable,
|
||||
type ColumnDef,
|
||||
} from "@tanstack/react-table";
|
||||
import type { ColumnMeta } from "@tanstack/react-table";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useQueryState, parseAsBoolean } from "nuqs";
|
||||
import { useMemo, useReducer, useRef, useState } from "react";
|
||||
@@ -45,6 +46,11 @@ import { TableActions } from "./UserTableActions";
|
||||
import type { UserTableState, UserTableAction, UserTableUser } from "./types";
|
||||
import { useFetchMoreOnBottomReached } from "./useFetchMoreOnBottomReached";
|
||||
|
||||
type CustomColumnMeta<TData, TValue> = ColumnMeta<TData, TValue> & {
|
||||
sticky?: boolean;
|
||||
stickLeft?: number;
|
||||
};
|
||||
|
||||
const initialState: UserTableState = {
|
||||
changeMemberRole: {
|
||||
showModal: false,
|
||||
@@ -186,6 +192,10 @@ export function UserListTable() {
|
||||
id: "select",
|
||||
enableHiding: false,
|
||||
enableSorting: false,
|
||||
meta: {
|
||||
sticky: true,
|
||||
stickLeft: 0,
|
||||
} as CustomColumnMeta<UserTableUser, unknown>,
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
checked={table.getIsAllPageRowsSelected()}
|
||||
@@ -210,6 +220,10 @@ export function UserListTable() {
|
||||
header: () => {
|
||||
return `Members`;
|
||||
},
|
||||
meta: {
|
||||
sticky: true,
|
||||
stickyLeft: 24,
|
||||
} as CustomColumnMeta<UserTableUser, unknown>,
|
||||
cell: ({ row }) => {
|
||||
const { username, email, avatarUrl } = row.original;
|
||||
return (
|
||||
|
||||
@@ -59,82 +59,102 @@ export function DataTable<TData, TValue>({
|
||||
...rest.style,
|
||||
}}
|
||||
data-testid={rest["data-testid"] ?? "data-table"}>
|
||||
<div className="overflow-hidden" style={{ gridArea: "body" }}>
|
||||
<div ref={tableContainerRef} onScroll={onScroll} className="scrollbar-thin h-full overflow-y-auto">
|
||||
<div className="inline-block min-w-full align-middle">
|
||||
<Table>
|
||||
<TableHeader className="sticky top-0 z-10">
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<TableHead
|
||||
key={header.id}
|
||||
className={classNames(
|
||||
header.column.getCanSort() ? "cursor-pointer select-none" : ""
|
||||
)}>
|
||||
<div className="flex items-center" onClick={header.column.getToggleSortingHandler()}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(header.column.columnDef.header, header.getContext())}
|
||||
{header.column.getIsSorted() && (
|
||||
<Icon
|
||||
name="arrow-up"
|
||||
className="ml-2 h-4 w-4"
|
||||
style={{
|
||||
transform:
|
||||
header.column.getIsSorted() === "asc" ? "rotate(0deg)" : "rotate(180deg)",
|
||||
transition: "transform 0.2s ease-in-out",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</TableHead>
|
||||
))}
|
||||
<div
|
||||
ref={tableContainerRef}
|
||||
onScroll={onScroll}
|
||||
className="scrollbar-thin border-subtle relative h-full overflow-auto rounded-md border"
|
||||
style={{ gridArea: "body" }}>
|
||||
<Table className="border-0">
|
||||
<TableHeader className="bg-subtle sticky top-0 z-10">
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
const meta = header.column.columnDef.meta as { sticky?: boolean; stickyLeft?: number };
|
||||
return (
|
||||
<TableHead
|
||||
key={header.id}
|
||||
style={{
|
||||
left: meta?.stickyLeft ? `${meta.stickyLeft}px` : undefined,
|
||||
}}
|
||||
className={classNames(
|
||||
header.column.getCanSort() ? "cursor-pointer select-none" : "",
|
||||
meta?.sticky && "bg-subtle sticky left-0 top-0 z-20"
|
||||
)}>
|
||||
<div className="flex items-center" onClick={header.column.getToggleSortingHandler()}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(header.column.columnDef.header, header.getContext())}
|
||||
{header.column.getIsSorted() && (
|
||||
<Icon
|
||||
name="arrow-up"
|
||||
className="ml-2 h-4 w-4"
|
||||
style={{
|
||||
transform:
|
||||
header.column.getIsSorted() === "asc" ? "rotate(0deg)" : "rotate(180deg)",
|
||||
transition: "transform 0.2s ease-in-out",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</TableHead>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{paddingTop > 0 && (
|
||||
<tr>
|
||||
<td style={{ height: `${paddingTop}px` }} />
|
||||
</tr>
|
||||
)}
|
||||
{virtualRows && !isPending ? (
|
||||
virtualRows.map((virtualRow) => {
|
||||
const row = rows[virtualRow.index] as Row<TData>;
|
||||
return (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
onClick={() => onRowMouseclick && onRowMouseclick(row)}
|
||||
className={classNames(
|
||||
onRowMouseclick && "hover:cursor-pointer",
|
||||
variant === "compact" && "!border-0",
|
||||
"group"
|
||||
)}>
|
||||
{row.getVisibleCells().map((cell) => {
|
||||
const column = table.getColumn(cell.column.id);
|
||||
const meta = column?.columnDef.meta as { sticky?: boolean; stickyLeft?: number };
|
||||
return (
|
||||
<TableCell
|
||||
key={cell.id}
|
||||
style={{
|
||||
left: meta?.stickyLeft ? `${meta.stickyLeft}px` : undefined,
|
||||
}}
|
||||
className={classNames(
|
||||
variant === "compact" && "p-1.5",
|
||||
meta?.sticky && "group-hover:bg-muted bg-default sticky left-0"
|
||||
)}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{paddingTop > 0 && (
|
||||
<tr>
|
||||
<td style={{ height: `${paddingTop}px` }} />
|
||||
</tr>
|
||||
)}
|
||||
{virtualRows && !isPending ? (
|
||||
virtualRows.map((virtualRow) => {
|
||||
const row = rows[virtualRow.index] as Row<TData>;
|
||||
return (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
onClick={() => onRowMouseclick && onRowMouseclick(row)}
|
||||
className={classNames(
|
||||
onRowMouseclick && "hover:cursor-pointer",
|
||||
variant === "compact" && "!border-0"
|
||||
)}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id} className={classNames(variant === "compact" && "p-1.5")}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={table.getAllColumns().length} className="h-24 text-center">
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
{paddingBottom > 0 && (
|
||||
<tr>
|
||||
<td style={{ height: `${paddingBottom}px` }} />
|
||||
</tr>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={table.getAllColumns().length} className="h-24 text-center">
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
{paddingBottom > 0 && (
|
||||
<tr>
|
||||
<td style={{ height: `${paddingBottom}px` }} />
|
||||
</tr>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import "@tanstack/react-table";
|
||||
|
||||
declare module "@tanstack/react-table" {
|
||||
interface ColumnMeta<TData extends RowData, TValue> {
|
||||
sticky?: boolean;
|
||||
stickyLeft?: number;
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,11 @@ import { classNames } from "@calcom/lib";
|
||||
|
||||
const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div className="w-full overflow-y-auto">
|
||||
<table
|
||||
ref={ref}
|
||||
className={classNames("border-subtle w-full caption-bottom border text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
<table
|
||||
ref={ref}
|
||||
className={classNames("border-subtle w-full caption-bottom border text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
Table.displayName = "Table";
|
||||
|
||||
Reference in New Issue
Block a user