Skip to content

Instantly share code, notes, and snippets.

@otonii
Created June 30, 2024 16:32
Show Gist options
  • Save otonii/f0c54cb617c6230ce487d4020460f2a9 to your computer and use it in GitHub Desktop.
Save otonii/f0c54cb617c6230ce487d4020460f2a9 to your computer and use it in GitHub Desktop.
my table component react
import { HTMLAttributes, ReactNode } from "react"
import { cn } from "@/lib/utils"
type ColumnValue = ReactNode
export interface Column<T> {
label: ReactNode
template?: (item: T, index: number, rows: T[]) => ColumnValue
cellClassName?: string
labelClassName?: string
key: string
}
export type ItemClickProps<T> = {
item: T
index: number
event: React.MouseEvent<HTMLDivElement, MouseEvent>
}
export interface TableProps<T> extends HTMLAttributes<HTMLTableElement> {
columns: Column<T>[]
data?: T[]
keyExtractor: (item: T) => string | number
fixedHeader?: boolean
onItemClick?: (props: ItemClickProps<T>) => void
selectedRows?: number[]
cellClassName?: string
labelClassName?: string
selectedCellClassName?: string
}
export function Table<T>({
className,
data,
columns,
keyExtractor,
fixedHeader,
onItemClick,
selectedRows = [],
cellClassName: rootCellClassName,
labelClassName: rootLabelClassName,
selectedCellClassName = "selected",
...props
}: TableProps<T>) {
function handleItemClick(props: ItemClickProps<T>) {
onItemClick?.(props)
}
function renderRow(item: T, index: number, items: T[]) {
const rowKey = keyExtractor(item)
const isSelected = selectedRows.includes(index)
return (
<tr
className={isSelected ? selectedCellClassName : ""}
key={rowKey}
onClick={event => handleItemClick({ item, index, event })}
>
{columns.map(({ key, cellClassName, template }) => {
return (
<td
key={`${rowKey}_${key}`}
className={cn(
"z-10 border-b border-r px-2 py-1 text-sm text-foreground last-of-type:border-r-0",
rootCellClassName,
cellClassName,
)}
>
{template && template(item, index, items)}
</td>
)
})}
</tr>
)
}
return (
<table
className={cn("border-separate border-spacing-0", className)}
{...props}
>
<thead>
<tr className="sticky top-0">
{columns.map(({ key, label, labelClassName }) => {
return (
<th
key={key}
className={cn(
"text-nowrap border-b border-r border-t bg-background px-2 py-1 text-left text-sm font-normal text-muted-foreground last-of-type:border-r-0",
rootLabelClassName,
labelClassName,
{
"sticky top-0 z-20": fixedHeader,
},
)}
>
{label}
</th>
)
})}
</tr>
</thead>
<tbody className="z-0">{data?.map(renderRow)}</tbody>
</table>
)
}
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment