"use client"; import React from "react"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Checkbox } from "@/components/ui/checkbox"; import { ArrowUp, ArrowDown, ArrowUpDown } from "lucide-react"; import { cn } from "@/lib/utils"; import { ColumnConfig } from "./types"; import { useScreenMultiLang } from "@/contexts/ScreenMultiLangContext"; interface SingleTableWithStickyProps { visibleColumns?: ColumnConfig[]; columns?: ColumnConfig[]; data: Record[]; columnLabels: Record; sortColumn: string | null; sortDirection: "asc" | "desc"; tableConfig?: any; isDesignMode?: boolean; isAllSelected?: boolean; handleSort?: (columnName: string) => void; onSort?: (columnName: string) => void; handleSelectAll?: (checked: boolean) => void; handleRowClick?: (row: any) => void; renderCheckboxCell?: (row: any, index: number) => React.ReactNode; renderCheckboxHeader?: () => React.ReactNode; formatCellValue: (value: any, format?: string, columnName?: string, rowData?: Record) => string; getColumnWidth: (column: ColumnConfig) => number; containerWidth?: string; // 컨테이너 너비 설정 loading?: boolean; error?: string | null; // 인라인 편집 관련 props onCellDoubleClick?: (rowIndex: number, colIndex: number, columnName: string, value: any) => void; editingCell?: { rowIndex: number; colIndex: number; columnName: string; originalValue: any } | null; editingValue?: string; onEditingValueChange?: (value: string) => void; onEditKeyDown?: (e: React.KeyboardEvent) => void; editInputRef?: React.RefObject; // 검색 하이라이트 관련 props searchHighlights?: Set; currentSearchIndex?: number; searchTerm?: string; } export const SingleTableWithSticky: React.FC = ({ visibleColumns, columns, data, columnLabels, sortColumn, sortDirection, tableConfig, isDesignMode = false, isAllSelected = false, handleSort, onSort, handleSelectAll, handleRowClick, renderCheckboxCell, renderCheckboxHeader, formatCellValue, getColumnWidth, containerWidth, loading = false, error = null, // 인라인 편집 관련 props onCellDoubleClick, editingCell, editingValue, onEditingValueChange, onEditKeyDown, editInputRef, // 검색 하이라이트 관련 props searchHighlights, currentSearchIndex = 0, searchTerm = "", }) => { const { getTranslatedText } = useScreenMultiLang(); const checkboxConfig = tableConfig?.checkbox || {}; const actualColumns = visibleColumns || columns || []; const sortHandler = onSort || handleSort || (() => {}); return (
{actualColumns.map((column, colIndex) => { // 왼쪽 고정 컬럼들의 누적 너비 계산 const leftFixedWidth = actualColumns .slice(0, colIndex) .filter((col) => col.fixed === "left") .reduce((sum, col) => sum + getColumnWidth(col), 0); // 오른쪽 고정 컬럼들의 누적 너비 계산 const rightFixedColumns = actualColumns.filter((col) => col.fixed === "right"); const rightFixedIndex = rightFixedColumns.findIndex((col) => col.columnName === column.columnName); const rightFixedWidth = rightFixedIndex >= 0 ? rightFixedColumns.slice(rightFixedIndex + 1).reduce((sum, col) => sum + getColumnWidth(col), 0) : 0; return ( column.sortable && sortHandler(column.columnName)} >
{column.columnName === "__checkbox__" ? ( checkboxConfig.selectAll && ( ) ) : ( <> {/* langKey가 있으면 다국어 번역 사용, 없으면 기존 라벨 */} {(column as any).langKey ? getTranslatedText( (column as any).langKey, columnLabels[column.columnName] || column.displayName || column.columnName, ) : columnLabels[column.columnName] || column.displayName || column.columnName} {column.sortable && sortColumn === column.columnName && ( {sortDirection === "asc" ? ( ) : ( )} )} )}
); })}
{data.length === 0 ? (
데이터가 없습니다 조건을 변경하여 다시 검색해보세요
) : ( data.map((row, index) => ( handleRowClick(row)} > {visibleColumns.map((column, colIndex) => { // 왼쪽 고정 컬럼들의 누적 너비 계산 const leftFixedWidth = visibleColumns .slice(0, colIndex) .filter((col) => col.fixed === "left") .reduce((sum, col) => sum + getColumnWidth(col), 0); // 오른쪽 고정 컬럼들의 누적 너비 계산 const rightFixedColumns = visibleColumns.filter((col) => col.fixed === "right"); const rightFixedIndex = rightFixedColumns.findIndex((col) => col.columnName === column.columnName); const rightFixedWidth = rightFixedIndex >= 0 ? rightFixedColumns .slice(rightFixedIndex + 1) .reduce((sum, col) => sum + getColumnWidth(col), 0) : 0; // 현재 셀이 편집 중인지 확인 const isEditing = editingCell?.rowIndex === index && editingCell?.colIndex === colIndex; // 검색 하이라이트 확인 - 실제 셀 값에 검색어가 포함되어 있는지도 확인 const cellKey = `${index}-${colIndex}`; const cellValue = String(row[column.columnName] ?? "").toLowerCase(); const hasSearchTerm = searchTerm ? cellValue.includes(searchTerm.toLowerCase()) : false; // 인덱스 기반 하이라이트 + 실제 값 검증 const isHighlighted = column.columnName !== "__checkbox__" && hasSearchTerm && (searchHighlights?.has(cellKey) ?? false); // 현재 검색 결과인지 확인 (currentSearchIndex가 -1이면 현재 페이지에 없음) const highlightArray = searchHighlights ? Array.from(searchHighlights) : []; const isCurrentSearchResult = isHighlighted && currentSearchIndex >= 0 && currentSearchIndex < highlightArray.length && highlightArray[currentSearchIndex] === cellKey; // 셀 값에서 검색어 하이라이트 렌더링 const renderCellContent = () => { const cellValue = formatCellValue(row[column.columnName], column.format, column.columnName, row) || "\u00A0"; if (!isHighlighted || !searchTerm || column.columnName === "__checkbox__") { return cellValue; } // 검색어 하이라이트 처리 const lowerValue = String(cellValue).toLowerCase(); const lowerTerm = searchTerm.toLowerCase(); const startIndex = lowerValue.indexOf(lowerTerm); if (startIndex === -1) return cellValue; const before = String(cellValue).slice(0, startIndex); const match = String(cellValue).slice(startIndex, startIndex + searchTerm.length); const after = String(cellValue).slice(startIndex + searchTerm.length); return ( <> {before} {match} {after} ); }; return ( { if (onCellDoubleClick && column.columnName !== "__checkbox__") { e.stopPropagation(); onCellDoubleClick(index, colIndex, column.columnName, row[column.columnName]); } }} > {column.columnName === "__checkbox__" ? ( renderCheckboxCell(row, index) ) : isEditing ? ( // 인라인 편집 입력 필드 onEditingValueChange?.(e.target.value)} onKeyDown={onEditKeyDown} onBlur={() => { // blur 시 저장 (Enter와 동일) if (onEditKeyDown) { const fakeEvent = { key: "Enter", preventDefault: () => {}, } as React.KeyboardEvent; onEditKeyDown(fakeEvent); } }} className="border-primary bg-background focus:ring-primary h-8 w-full rounded border px-2 text-xs focus:ring-2 focus:outline-none sm:text-sm" onClick={(e) => e.stopPropagation()} /> ) : ( renderCellContent() )} ); })} )) )}
); };