feat(v3): table designs (#21147)

This commit is contained in:
sean-brydon
2025-05-07 09:54:36 +00:00
committed by GitHub
parent 390ee79134
commit 2bce38fd2a
7 changed files with 45 additions and 33 deletions
@@ -39,6 +39,7 @@ export type DataTablePropsFromWrapper<TData> = {
headerClassName?: string;
rowClassName?: string;
paginationMode?: "infinite" | "standard";
hasWrapperContext?: boolean;
};
export type DataTableProps<TData> = DataTablePropsFromWrapper<TData> & {
@@ -64,6 +65,7 @@ export function DataTable<TData>({
headerClassName,
rowClassName,
paginationMode = "infinite",
hasWrapperContext = false,
...rest
}: DataTableProps<TData> & React.ComponentPropsWithoutRef<"div">) {
const { rows } = table.getRowModel();
@@ -104,7 +106,10 @@ export function DataTable<TData>({
return (
<div
className={classNames("grid", className)}
className={classNames(
!hasWrapperContext ? "grid" : "bg-muted grid rounded-xl px-0.5 pb-0.5",
className
)}
style={{
gridTemplateRows: "auto 1fr auto",
gridTemplateAreas: "'header' 'body' 'footer'",
@@ -129,20 +134,23 @@ export function DataTable<TData>({
onScroll={onScroll}
className={classNames(
"relative overflow-auto",
"scrollbar-thin border-subtle relative rounded-md border",
"scrollbar-thin relative rounded-md ",
paginationMode === "infinite" && "h-[80dvh]", // Set a fixed height for the container
containerClassName
)}
style={{ gridArea: "body" }}>
<TableNew
className="data-table grid border-0"
className={classNames(
"data-table grid border-0",
!hasWrapperContext && "bg-muted rounded-xl px-0.5 pb-0.5"
)}
style={{
...columnSizingVars,
...(Boolean(enableColumnResizing) && { width: table.getTotalSize() }),
}}>
<TableHeader className={classNames("sticky top-0 z-10", headerClassName)}>
{table.getHeaderGroups().map((headerGroup: HeaderGroup<TData>) => (
<TableRow key={headerGroup.id} className="hover:bg-subtle flex w-full">
<TableRow key={headerGroup.id} className="hover:bg-subtle flex w-full border-none">
{headerGroup.headers.map((header: Header<TData, unknown>) => {
const { column } = header;
return (
@@ -155,7 +163,6 @@ export function DataTable<TData>({
}}
className={classNames(
"relative flex shrink-0 items-center",
"bg-subtle",
column.getIsPinned() && "top-0 z-20 sm:sticky"
)}>
<TableHeadLabel header={header} />
@@ -164,7 +171,7 @@ export function DataTable<TData>({
onMouseDown={header.getResizeHandler()}
onTouchStart={header.getResizeHandler()}
className={classNames(
"group absolute right-0 top-0 h-full w-[5px] cursor-col-resize touch-none select-none opacity-[0.1] hover:opacity-50",
"group absolute right-0 top-0 h-full w-[5px] cursor-col-resize touch-none select-none opacity-0 hover:opacity-50",
header.column.getIsResizing() && "!opacity-75"
)}>
<div className="bg-inverted mx-auto h-full w-[1px]" />
@@ -279,7 +286,10 @@ function DataTableBody<TData>({
}
return (
<TableBody className="relative grid" data-testid={testId} style={{ height: tableHeight }}>
<TableBody
className="border-subtle relative grid overflow-hidden rounded-xl border"
data-testid={testId}
style={{ height: tableHeight }}>
{rowsToRender.map(({ row, virtualItem }) => (
<TableRow
ref={virtualItem ? (node) => rowVirtualizer.measureElement(node) : undefined}
@@ -307,7 +317,7 @@ function DataTableBody<TData>({
width: `var(--col-${kebabCase(cell.column.id)}-size)`,
}}
className={classNames(
"flex shrink-0 items-center overflow-hidden",
"bg-default group-hover:!bg-muted group-data-[state=selected]:bg-subtle flex shrink-0 items-center overflow-hidden",
variant === "compact" && "p-0",
column.getIsPinned() &&
"bg-default group-hover:!bg-muted group-data-[state=selected]:bg-subtle sm:sticky"
@@ -351,7 +361,7 @@ const TableHeadLabel = ({ header }: { header: Header<any, any> }) => {
open && "bg-muted"
)}>
<div
className="truncate"
className="text-default truncate text-sm leading-none"
title={
typeof header.column.columnDef.header === "string" ? header.column.columnDef.header : undefined
}>
@@ -1,6 +1,6 @@
"use client";
import type { VisibilityState } from "@tanstack/react-table";
import type { Row, VisibilityState } from "@tanstack/react-table";
// eslint-disable-next-line no-restricted-imports
import { noop } from "lodash";
import { useEffect, useRef } from "react";
@@ -22,6 +22,7 @@ type BaseDataTableWrapperProps<TData> = Omit<
EmptyView?: React.ReactNode;
LoaderView?: React.ReactNode;
tableContainerRef?: React.RefObject<HTMLDivElement>;
onRowMouseclick?: (row: Row<TData>) => void;
};
type InfinitePaginationProps<TData> = BaseDataTableWrapperProps<TData> & {
@@ -61,6 +62,7 @@ export function DataTableWrapper<TData>({
children,
tableContainerRef: externalRef,
paginationMode,
onRowMouseclick,
}: DataTableWrapperProps<TData>) {
const internalRef = useRef<HTMLDivElement>(null);
const tableContainerRef = externalRef || internalRef;
@@ -130,13 +132,15 @@ export function DataTableWrapper<TData>({
headerClassName={headerClassName}
rowClassName={rowClassName}
paginationMode={paginationMode}
onRowMouseclick={onRowMouseclick}
hasWrapperContext={true}
onScroll={
paginationMode === "infinite"
? (e: Pick<React.UIEvent<HTMLDivElement, UIEvent>, "target">) =>
fetchMoreOnBottomReached(e.target as HTMLDivElement)
: undefined
}>
<div style={{ gridArea: "footer", marginTop: "1rem" }}>
<div style={{ gridArea: "footer" }} className="px-3 py-2">
<DataTablePagination<TData>
table={table}
totalRowCount={totalRowCount}
@@ -19,10 +19,10 @@ import type { Dispatch, SetStateAction } from "react";
import { checkAdminOrOwner } from "@calcom/features/auth/lib/checkAdminOrOwner";
import { Dialog } from "@calcom/features/components/controlled-dialog";
import {
DataTable,
DataTableProvider,
DataTableToolbar,
DataTableFilters,
DataTableWrapper,
DataTableSelectionBar,
useDataTable,
useFetchMoreOnBottomReached,
@@ -305,7 +305,6 @@ function MemberListContent(props: Props) {
checked={table.getIsAllPageRowsSelected()}
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
aria-label="Select all"
className="translate-y-[2px]"
/>
),
cell: ({ row }) => (
@@ -660,18 +659,27 @@ function MemberListContent(props: Props) {
return (
<>
<DataTable
<DataTableWrapper
testId="team-member-list-container"
table={table}
tableContainerRef={tableContainerRef}
isPending={isPending}
enableColumnResizing={true}
onScroll={(e) => fetchMoreOnBottomReached(e.target as HTMLDivElement)}>
<DataTableToolbar.Root>
<div className="flex w-full gap-2">
paginationMode="infinite"
hasNextPage={hasNextPage}
fetchNextPage={fetchNextPage}
isFetching={isFetching}
totalRowCount={totalRowCount}
ToolbarLeft={
<>
<DataTableToolbar.SearchBar />
<DataTableFilters.AddFilterButton table={table} />
<DataTableFilters.ColumnVisibilityButton table={table} />
<DataTableFilters.FilterBar table={table} />
</>
}
ToolbarRight={
<>
<DataTableFilters.ClearFiltersButton />
{isAdminOrOwner && (
<DataTableToolbar.CTA
type="button"
@@ -682,12 +690,8 @@ function MemberListContent(props: Props) {
{t("add")}
</DataTableToolbar.CTA>
)}
</div>
<div className="flex gap-2 justify-self-start">
<DataTableFilters.ActiveFilters table={table} />
</div>
</DataTableToolbar.Root>
</>
}>
{numberOfSelectedRows >= 2 && dynamicLinkVisible && (
<DataTableSelectionBar.Root className="!bottom-[7.3rem] md:!bottom-32">
<DynamicLink table={table} domain={domain} />
@@ -715,7 +719,7 @@ function MemberListContent(props: Props) {
/>
</DataTableSelectionBar.Root>
)}
</DataTable>
</DataTableWrapper>
{state.deleteMember.showModal && (
<Dialog
open={true}
@@ -113,7 +113,6 @@ function UserListTableContent({ oAuthClientId }: PlatformManagedUsersTableProps)
checked={table.getIsAllPageRowsSelected()}
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
aria-label="Select all"
className="translate-y-[2px]"
/>
),
cell: ({ row }) => (
@@ -257,7 +257,6 @@ function UserListTableContent() {
checked={table.getIsAllPageRowsSelected()}
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
aria-label="Select all"
className="translate-y-[2px]"
/>
),
cell: ({ row }) => (
@@ -68,7 +68,7 @@ export const Pagination = ({
const endItem = Math.min(currentPage * pageSize, totalItems);
return (
<div className="flex items-center justify-between space-x-2 py-4">
<div className="flex items-center justify-between space-x-2">
<div className="flex items-center space-x-2">
<Select
options={pageSizeSelectOptions}
+1 -5
View File
@@ -15,11 +15,7 @@ Table.displayName = "Table";
const TableHeader = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
({ className, ...props }, ref) => (
<thead
ref={ref}
className={classNames("[&_tr]:bg-subtle md:z-10 [&_tr]:border-b", className)}
{...props}
/>
<thead ref={ref} className={classNames("md:z-10", className)} {...props} />
)
);
TableHeader.displayName = "TableHeader";