spb/datacenterindex
Public
HTML 53.9%
TypeScript 44.5%
JavaScript 0.6%
SQL 0.5%
1import Link from "next/link";2import type { HTMLAttributes, ReactNode, TdHTMLAttributes, ThHTMLAttributes } from "react";3import { cn } from "./cn";4import { CollapsibleRows } from "./collapsible-rows";56/**7 * Dense data table. Sticky header (via .dci-table th), hairline rows, tabular numerics.8 * `responsive` turns rows into compact two-line rows under 640 px — the first line is the9 * <Td primary> cell with the optional <Td lead> figure on the right, the other cells wrap under it10 * as "LABEL value" pairs (every <Td> must carry `label`). Cells hidden with `hidden sm:table-cell`11 * stay hidden on phones.12 * `mobileCollapse` shows only the first 10 rows under 640 px with a "Show all N" toggle.13 */14export function Table({ className, responsive = true, children, dense, mobileCollapse, ...rest }: HTMLAttributes<HTMLTableElement> & { responsive?: boolean; dense?: boolean; mobileCollapse?: { total: number; label?: string } }) {15 const table = (16 <div className={cn("w-full", !responsive && "overflow-x-auto")}>17 <table className={cn("dci-table text-[13px]", responsive && "responsive", dense && "dense", className)} {...rest}>18 {children}19 </table>20 </div>21 );22 if (mobileCollapse && responsive) {23 return (24 <CollapsibleRows total={mobileCollapse.total} label={mobileCollapse.label}>25 {table}26 </CollapsibleRows>27 );28 }29 return table;30}3132export function THead({ children, className }: { children: ReactNode; className?: string }) {33 return <thead className={cn("text-left", className)}>{children}</thead>;34}35export function TBody({ children, className }: { children: ReactNode; className?: string }) {36 return <tbody className={cn("divide-y divide-hair", className)}>{children}</tbody>;37}38export function Tr({ children, className, href, ...rest }: HTMLAttributes<HTMLTableRowElement> & { href?: string }) {39 return (40 <tr className={cn("group align-top transition-colors sm:hover:bg-surface-2/60", href && "cursor-pointer", className)} {...rest}>41 {children}42 </tr>43 );44}4546export interface ThProps extends ThHTMLAttributes<HTMLTableCellElement> {47 numeric?: boolean;48 /** sort state */49 sort?: "asc" | "desc" | null;50 sortHref?: string;51}52export function Th({ children, className, numeric, sort, sortHref, ...rest }: ThProps) {53 const inner = (54 <span className={cn("inline-flex items-center gap-1", numeric && "flex-row-reverse")}>55 {children}56 {sort !== undefined && (57 <span aria-hidden className={cn("text-[9px]", sort ? "text-ink" : "text-ink-4")}>58 {sort === "asc" ? "▲" : sort === "desc" ? "▼" : "◆"}59 </span>60 )}61 </span>62 );63 return (64 <th65 scope="col"66 aria-sort={sort === "asc" ? "ascending" : sort === "desc" ? "descending" : undefined}67 className={cn("label-caps whitespace-nowrap border-b border-hair px-2 py-2 font-medium first:pl-0 last:pr-0", numeric && "text-right", className)}68 {...rest}69 >70 {sortHref ? (71 <Link href={sortHref} className="hover:text-ink" scroll={false}>72 {inner}73 </Link>74 ) : (75 inner76 )}77 </th>78 );79}8081export interface TdProps extends TdHTMLAttributes<HTMLTableCellElement> {82 label?: string;83 numeric?: boolean;84 primary?: boolean;85 muted?: boolean;86 /** key figure of the row: sits right of the primary cell on the first line under 640 px */87 lead?: boolean;88}89export function Td({ children, className, label, numeric, primary, muted, lead, ...rest }: TdProps) {90 return (91 <td92 data-label={label}93 data-primary={primary ? "" : undefined}94 data-lead={lead ? "" : undefined}95 className={cn("px-2 py-2 first:pl-0 last:pr-0", numeric && "figure text-right", muted && "text-ink-3", className)}96 {...rest}97 >98 {children}99 </td>100 );101}102103/** Row link (keeps the table semantic). Under 640 px, inside a <Td primary>, it stretches over the whole row (`.dci-rowlink`). */104export function RowLink({ href, children, className }: { href: string; children: ReactNode; className?: string }) {105 return (106 <Link href={href} className={cn("dci-rowlink font-medium text-ink hover:text-accent hover:underline", className)}>107 {children}108 </Link>109 );110}111