|
| 1 | +import { |
| 2 | + HTMLAttributes, |
| 3 | + ReactNode, |
| 4 | + TableHTMLAttributes, |
| 5 | + TdHTMLAttributes, |
| 6 | +} from "react"; |
| 7 | + |
| 8 | +import { cn } from "@/utils/cn"; |
| 9 | + |
| 10 | +interface TableProps<T> extends TableHTMLAttributes<HTMLTableElement> { |
| 11 | + data: T[]; |
| 12 | + headRow: () => ReactNode; |
| 13 | + bodyRow: (params: T) => ReactNode; |
| 14 | + footerRow?: () => ReactNode; |
| 15 | +} |
| 16 | + |
| 17 | +function Table<T>({ |
| 18 | + data, |
| 19 | + className, |
| 20 | + headRow, |
| 21 | + bodyRow, |
| 22 | + footerRow, |
| 23 | + ...props |
| 24 | +}: TableProps<T>) { |
| 25 | + return ( |
| 26 | + <div className="rounded-lg border-[1px] border-gray-200 text-black overflow-hidden"> |
| 27 | + {/* ───── 스크롤이 필요한 영역 ───── */} |
| 28 | + <div className="overflow-x-auto"> |
| 29 | + <table |
| 30 | + className={cn( |
| 31 | + "table-fixed min-w-full text-left border-collapse", |
| 32 | + className, |
| 33 | + )} |
| 34 | + {...props} |
| 35 | + > |
| 36 | + <thead className="bg-red-50">{headRow()}</thead> |
| 37 | + <tbody className="bg-white">{data.map(bodyRow)}</tbody> |
| 38 | + </table> |
| 39 | + </div> |
| 40 | + |
| 41 | + {/* ──── 스크롤과 무관한 하단 영역 ───── */} |
| 42 | + {footerRow && ( |
| 43 | + <table className="w-full border-collapse"> |
| 44 | + <tbody>{footerRow()}</tbody> |
| 45 | + </table> |
| 46 | + )} |
| 47 | + </div> |
| 48 | + ); |
| 49 | +} |
| 50 | + |
| 51 | +interface TrProps extends HTMLAttributes<HTMLTableRowElement> { |
| 52 | + showLastBottomBorder?: boolean; |
| 53 | +} |
| 54 | + |
| 55 | +function Tr({ children, className, showLastBottomBorder, ...props }: TrProps) { |
| 56 | + const tableRowClassName = cn( |
| 57 | + showLastBottomBorder ? "border-b-[1px]" : "not-last:border-b-[1px]", |
| 58 | + "border-gray-20", |
| 59 | + className, |
| 60 | + ); |
| 61 | + |
| 62 | + return ( |
| 63 | + <tr className={tableRowClassName} {...props}> |
| 64 | + {children} |
| 65 | + </tr> |
| 66 | + ); |
| 67 | +} |
| 68 | + |
| 69 | +function Th({ |
| 70 | + children, |
| 71 | + className, |
| 72 | + ...props |
| 73 | +}: TdHTMLAttributes<HTMLTableCellElement>) { |
| 74 | + const thClassName = cn( |
| 75 | + "px-3.5 py-3 text-sm font-normal", |
| 76 | + "first:sticky left-0 border-gray-20 z-10 bg-red-50", |
| 77 | + className, |
| 78 | + ); |
| 79 | + |
| 80 | + return ( |
| 81 | + <th className={thClassName} {...props}> |
| 82 | + {children} |
| 83 | + </th> |
| 84 | + ); |
| 85 | +} |
| 86 | + |
| 87 | +interface TdProps extends TdHTMLAttributes<HTMLTableCellElement> { |
| 88 | + noSticky?: boolean; |
| 89 | +} |
| 90 | + |
| 91 | +function Td({ children, className, noSticky, ...props }: TdProps) { |
| 92 | + const tdClassName = cn( |
| 93 | + "px-3.5 py-3 break-words whitespace-normal", |
| 94 | + noSticky ? "" : "first:sticky left-0 z-10 bg-white first:whitespace-nowrap", |
| 95 | + className, |
| 96 | + ); |
| 97 | + return ( |
| 98 | + <td className={tdClassName} {...props}> |
| 99 | + {children} |
| 100 | + </td> |
| 101 | + ); |
| 102 | +} |
| 103 | + |
| 104 | +Table.Tr = Tr; |
| 105 | +Table.Th = Th; |
| 106 | +Table.Td = Td; |
| 107 | + |
| 108 | +export default Table; |
0 commit comments