Blocks

Table 7

Tables
Text Align LeftTableButtonsPagination
Table 7

Code

relume-table7.tsx
import {
  Accordion,
  AccordionContent,
  AccordionItem,
  AccordionTrigger,
} from "@/components/ui/accordion";
import { Button, type ButtonProps } from "@/components/ui/button";
import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from "@/components/ui/pagination";
import { Table, TableBody, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { KeyboardArrowDown } from "relume-icons";

type ImageProps = {
  src: string;
  alt: string;
};

type DetailedTableData = {
  name: string;
  company: string;
  number: number;
  team: string;
  date: string;
  link: string;
  details: {
    dateJoined: {
      label: string;
      value: string;
    };
    jobTitle: {
      label: string;
      value: string;
    };
    level: {
      label: string;
      value: number;
    };
    address: {
      label: string;
      value: string;
    };
    phone: {
      label: string;
      value: string;
    };
    email: {
      label: string;
      value: string;
    };
    skills: {
      label: string;
      value: string;
    };
    languages: {
      label: string;
      value: string;
    };
    experience: {
      label: string;
      value: string;
    };
    image: ImageProps;
  };
};

type RowGroup = {
  rows: DetailedTableData[];
};

type Props = {
  headerTitle: string;
  headerDescription: string;
  buttons: ButtonProps[];
  tableHeaders: string[];
  subHeadings: RowGroup[];
  paginationItems: number[];
};

export type Table7Props = React.ComponentPropsWithoutRef<"section"> & Partial<Props>;

const TABLE_SUBHEADING_CLASSES = [
  "w-[250px] pl-14",
  "w-[250px] pl-8 lg:pl-12",
  "w-[250px] pl-12",
  "w-[250px] pl-10",
  "w-[250px] pl-8",
];

export const Table7 = (props: Table7Props) => {
  const { headerTitle, headerDescription, buttons, tableHeaders, subHeadings, paginationItems } = {
    ...Table7Defaults,
    ...props,
  } as Props;

  return (
    <section className="px-[5%] py-16 md:py-24 lg:py-28">
      <div className="relative container">
        <div className="flex flex-col items-start justify-between gap-4 border border-b-0 border-scheme-border p-6 sm:flex-row sm:items-center">
          <div>
            <h1 className="text-large mb-1 font-semibold">{headerTitle}</h1>
            <p>{headerDescription}</p>
          </div>
          <div className="flex gap-4">
            {buttons.map((buttonProps, index) => (
              <Button key={index} {...buttonProps} />
            ))}
          </div>
        </div>
        <div>
          <Table className="border-collapse">
            <TableHeader>
              <TableRow className="border-b-0">
                {tableHeaders.map((header, index) => (
                  <TableHead
                    key={index}
                    className={`w-[250px] ${index === 1 ? "pl-8 lg:pl-14" : "pl-14"}`}
                  >
                    {header}
                  </TableHead>
                ))}
              </TableRow>
            </TableHeader>
            <TableBody className="w-full">
              <tr>
                <td colSpan={6} className="p-0">
                  <Accordion type="multiple" className="w-full">
                    {subHeadings.map((subHeading, index) => (
                      <AccordionItem
                        key={index}
                        value={`group-${index}`}
                        className="w-full border-t border-b border-scheme-border first:border-b-0 last:border-t-0"
                      >
                        <AccordionTrigger
                          className="flex w-full items-center justify-start border-none p-0"
                          icon={
                            <KeyboardArrowDown className="absolute order-first ml-5 size-5 text-scheme-text transition-transform duration-300" />
                          }
                        >
                          <table className="w-full">
                            <tbody>
                              {subHeading.rows.map((row, rowIndex) => (
                                <TableHeadData
                                  key={rowIndex}
                                  row={row}
                                  cellClasses={TABLE_SUBHEADING_CLASSES}
                                />
                              ))}
                            </tbody>
                          </table>
                        </AccordionTrigger>
                        <AccordionContent className="w-full border-none p-0">
                          {subHeading.rows.map((row, rowIndex) => (
                            <ExpandedContent key={rowIndex} details={row.details} />
                          ))}
                        </AccordionContent>
                      </AccordionItem>
                    ))}
                  </Accordion>
                </td>
              </tr>
            </TableBody>
          </Table>
          <Pagination className="mt-10">
            <PaginationContent>
              <PaginationItem>
                <PaginationPrevious href="#" size="sm" variant="secondary" />
              </PaginationItem>
              <PaginationItem className="hidden md:block">
                {paginationItems.map((item, index) => (
                  <PaginationLink
                    key={index}
                    href="#"
                    size="sm"
                    variant="link"
                    className="px-4 py-2"
                  >
                    {item}
                  </PaginationLink>
                ))}
              </PaginationItem>
              <PaginationItem>
                <PaginationNext href="#" size="sm" variant="secondary" />
              </PaginationItem>
            </PaginationContent>
          </Pagination>
        </div>
      </div>
    </section>
  );
};

const TableHeadData = ({
  row,
  cellClasses,
}: {
  row: DetailedTableData;
  cellClasses: readonly string[];
}) => (
  <TableRow className="w-full border-none [&_th]:h-20 [&_th]:font-normal [&_th]:no-underline">
    <TableHead className={`${cellClasses[0]}`}>{row.name}</TableHead>
    <TableHead className={`${cellClasses[1]}`}>{row.company}</TableHead>
    <TableHead className={`${cellClasses[2]}`}>{row.number}</TableHead>
    <TableHead className={`${cellClasses[3]}`}>{row.team}</TableHead>
    <TableHead className={`${cellClasses[4]}`}>{row.date}</TableHead>
  </TableRow>
);

const DetailRow = ({ label, value }: { label: string; value: string | number }) => (
  <div className="mb-4">
    <p className="text-small mb-1 font-semibold">{label}</p>
    <p className="text-regular">{value}</p>
  </div>
);

const ExpandedContent = ({ details }: { details: DetailedTableData["details"] }) => (
  <div className="grid flex-1 auto-cols-fr grid-cols-5 items-start bg-scheme-foreground py-8 pr-0 pl-8">
    <div className="flex flex-col items-start gap-4 px-6">
      <DetailRow label={details.dateJoined.label} value={details.dateJoined.value} />
      <DetailRow label={details.address.label} value={details.address.value} />
      <DetailRow label={details.skills.label} value={details.skills.value} />
    </div>
    <div className="flex flex-col items-start gap-4 px-6">
      <DetailRow label={details.jobTitle.label} value={details.jobTitle.value} />
      <DetailRow label={details.phone.label} value={details.phone.value} />
      <DetailRow label={details.languages.label} value={details.languages.value} />
    </div>
    <div className="flex flex-col items-start gap-4 px-6">
      <DetailRow label={details.level.label} value={details.level.value} />
      <DetailRow label={details.email.label} value={details.email.value} />
      <DetailRow label={details.experience.label} value={details.experience.value} />
    </div>

    <div className="col-start-4 col-end-6 row-start-1 row-end-2 px-6">
      <div className="relative w-full overflow-hidden pb-[56.25%]">
        <img
          src={details.image.src}
          alt={details.image.alt}
          className="absolute inset-0 size-full object-cover"
        />
      </div>
    </div>
  </div>
);

export const Table7Defaults = {
  headerTitle: "Table Header",
  headerDescription: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
  buttons: [
    { children: "Button", variant: "secondary", size: "sm" },
    { children: "Button", size: "sm" },
  ],
  tableHeaders: ["Name", "Company", "Number", "Team", "Date"],
  subHeadings: [
    {
      rows: [
        {
          name: "First name",
          company: "Company name",
          number: 14,
          team: "Team name",
          date: "Jan 11, 2050",
          link: "#",
          details: {
            dateJoined: {
              label: "Date Joined",
              value: "Jan 11, 2050",
            },
            jobTitle: {
              label: "Job title",
              value: "Web developer",
            },
            level: {
              label: "Level",
              value: 2,
            },
            address: {
              label: "Address",
              value: "123 Sample St, Sydney",
            },
            phone: {
              label: "Phone",
              value: "+1 (555) 000-0000",
            },
            email: {
              label: "Email",
              value: "hello@relume.io",
            },
            skills: {
              label: "Skills",
              value: "Webflow, Figma",
            },
            languages: {
              label: "Languages",
              value: "English",
            },
            experience: {
              label: "Experience",
              value: "5+ years",
            },
            image: {
              src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
              alt: "Relume placeholder image",
            },
          },
        },
      ],
    },
    {
      rows: [
        {
          name: "First name",
          company: "Company name",
          number: 14,
          team: "Team name",
          date: "Jan 11, 2050",
          link: "#",
          details: {
            dateJoined: {
              label: "Date Joined",
              value: "Jan 11, 2050",
            },
            jobTitle: {
              label: "Job title",
              value: "Web developer",
            },
            level: {
              label: "Level",
              value: 2,
            },
            address: {
              label: "Address",
              value: "123 Sample St, Sydney",
            },
            phone: {
              label: "Phone",
              value: "+1 (555) 000-0000",
            },
            email: {
              label: "Email",
              value: "hello@relume.io",
            },
            skills: {
              label: "Skills",
              value: "Webflow, Figma",
            },
            languages: {
              label: "Languages",
              value: "English",
            },
            experience: {
              label: "Experience",
              value: "5+ years",
            },
            image: {
              src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
              alt: "Relume placeholder image",
            },
          },
        },
      ],
    },
    {
      rows: [
        {
          name: "First name",
          company: "Company name",
          number: 14,
          team: "Team name",
          date: "Jan 11, 2050",
          link: "#",
          details: {
            dateJoined: {
              label: "Date Joined",
              value: "Jan 11, 2050",
            },
            jobTitle: {
              label: "Job title",
              value: "Web developer",
            },
            level: {
              label: "Level",
              value: 2,
            },
            address: {
              label: "Address",
              value: "123 Sample St, Sydney",
            },
            phone: {
              label: "Phone",
              value: "+1 (555) 000-0000",
            },
            email: {
              label: "Email",
              value: "hello@relume.io",
            },
            skills: {
              label: "Skills",
              value: "Webflow, Figma",
            },
            languages: {
              label: "Languages",
              value: "English",
            },
            experience: {
              label: "Experience",
              value: "5+ years",
            },
            image: {
              src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-landscape.svg",
              alt: "Relume placeholder image",
            },
          },
        },
      ],
    },
  ],
  paginationItems: [1, 2, 3, 4, 5],
};

npm i @radix-ui/react-accordion @radix-ui/react-dropdown-menu @radix-ui/react-slot @radix-ui/react-tabs class-variance-authority@0.7.1 clsx@^2.1.1 relume-icons@1.3.0 tailwind-merge@^2.2.2