/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
import * as React from 'react'
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table'
import { cn } from '@/lib/utils'
import { TruncatedCell } from '../core/truncated-cell'
import { staticDataTableClassNames } from './static-data-table-classnames'
type StaticDataTableBaseProps = {
className?: string
tableClassName?: string
containerProps?: Omit, 'className' | 'children'>
tableProps?: Omit<
React.ComponentProps,
'className' | 'children'
>
}
type StaticDataTableDataProps = StaticDataTableBaseProps & {
columns: StaticDataTableColumn[]
data: TData[]
getRowKey?: (row: TData, index: number) => React.Key
getRowClassName?: (row: TData, index: number) => string | undefined
renderRow?: (row: TData, index: number) => React.ReactNode
empty?: boolean
emptyContent?: React.ReactNode
emptyClassName?: string
headerRowClassName?: string
}
type StaticDataTableChildrenProps = StaticDataTableBaseProps & {
children: React.ReactNode
columns?: never
data?: never
}
type StaticDataTableProps =
| StaticDataTableDataProps
| StaticDataTableChildrenProps
export type StaticDataTableColumn = {
id: string
header: React.ReactNode
className?: string
cellClassName?: string | ((row: TData, index: number) => string | undefined)
cell?: (row: TData, index: number) => React.ReactNode
}
export function StaticDataTable(
props: StaticDataTableProps
) {
const { className, tableClassName, containerProps, tableProps } = props
return (
{props.columns !== undefined ? (
) : (
props.children
)}
)
}
function StaticDataTableWithColumns({
columns,
data,
getRowKey,
getRowClassName,
renderRow,
empty,
emptyContent,
emptyClassName,
headerRowClassName,
}: StaticDataTableDataProps) {
const isEmpty = empty ?? (data !== undefined && data.length === 0)
const bodyRows = data.map((row, index) => (
))
return (
<>
{columns.map((column) => (
{column.header}
))}
{isEmpty ? (
{emptyContent}
) : (
bodyRows
)}
>
)
}
type StaticDataTableRowProps = Required<
Pick, 'columns'>
> &
Pick, 'getRowClassName' | 'renderRow'> & {
row: TData
index: number
}
function StaticDataTableRow({
row,
index,
columns,
getRowClassName,
renderRow,
}: StaticDataTableRowProps) {
if (renderRow) {
return <>{renderRow(row, index)}>
}
return (
{columns.map((column) => (
{renderStaticCellContent(column, row, index)}
))}
)
}
function renderStaticCellContent(
column: StaticDataTableColumn,
row: TData,
index: number
) {
const content = column.cell?.(row, index)
const textContent = getPrimitiveTextContent(content)
if (!textContent) return content
return {content}
}
function getPrimitiveTextContent(content: React.ReactNode): string | null {
if (typeof content === 'string' || typeof content === 'number') {
return String(content)
}
if (
React.isValidElement<{ children?: React.ReactNode }>(content) &&
(typeof content.props.children === 'string' ||
typeof content.props.children === 'number')
) {
return String(content.props.children)
}
return null
}
function getStaticCellClassName(
column: StaticDataTableColumn,
row: TData,
index: number
) {
return typeof column.cellClassName === 'function'
? column.cellClassName(row, index)
: column.cellClassName
}
type StaticDataTableEmptyRowProps = {
colSpan: number
children: React.ReactNode
className?: string
}
function StaticDataTableEmptyRow({
colSpan,
children,
className,
}: StaticDataTableEmptyRowProps) {
return (
{children}
)
}