import type { TFunction } from 'i18next' import type { RefObject } from 'react' import type { SQLiteValue } from '../../hooks/use-sqlite-database' import { useVirtualizer } from '@tanstack/react-virtual' import * as React from 'react' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' type DataTableProps = { columns: string[] values: SQLiteValue[][] scrollRef: RefObject isTruncated?: boolean } const MAX_CELL_LENGTH = 120 const formatValue = (value: SQLiteValue, t: TFunction<'workflow'>): string => { if (value === null) return t('skillSidebar.sqlitePreview.nullValue') if (value instanceof Uint8Array) return t('skillSidebar.sqlitePreview.blobValue', { size: value.byteLength }) if (typeof value === 'bigint') return value.toString() return String(value) } const truncateValue = (value: string): string => { if (value.length <= MAX_CELL_LENGTH) return value return `${value.slice(0, MAX_CELL_LENGTH)}…` } const DataTable = ({ columns, values, scrollRef, isTruncated = false }: DataTableProps) => { const { t } = useTranslation('workflow') const keyColumnIndex = useMemo(() => { const candidates = new Set(['id', 'rowid', 'uuid']) return columns.findIndex(column => candidates.has(column.toLowerCase())) }, [columns]) const rowVirtualizer = useVirtualizer({ count: values.length, getScrollElement: () => scrollRef.current, estimateSize: () => 32, overscan: 8, }) const virtualRows = rowVirtualizer.getVirtualItems() const paddingTop = virtualRows.length > 0 ? virtualRows[0].start : 0 const paddingBottom = virtualRows.length > 0 ? rowVirtualizer.getTotalSize() - virtualRows[virtualRows.length - 1].end : 0 return ( {columns.map(column => ( ))} {paddingTop > 0 && ( )} {virtualRows.map((virtualRow) => { const row = values[virtualRow.index] const rowKey = keyColumnIndex >= 0 ? String(row[keyColumnIndex] ?? virtualRow.index) : String(virtualRow.index) return ( {row.map((value, cellIndex) => { const rawValue = formatValue(value, t) const displayValue = truncateValue(rawValue) return ( ) })} ) })} {paddingBottom > 0 && ( )} {isTruncated && ( )}
{column}
{displayValue}
{t('skillSidebar.sqlitePreview.rowsTruncated', { limit: values.length })}
) } export default React.memo(DataTable)