테이블 틀고정기능

This commit is contained in:
kjs
2025-12-18 10:15:33 +09:00
parent 061fd45bc8
commit c3f066f88f
3 changed files with 127 additions and 24 deletions

View File

@@ -687,7 +687,11 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
const [isTableOptionsOpen, setIsTableOptionsOpen] = useState(false);
const [showGridLines, setShowGridLines] = useState(true);
const [viewMode, setViewMode] = useState<"table" | "card" | "grouped-card">("table");
const [frozenColumns, setFrozenColumns] = useState<string[]>([]);
// 체크박스 컬럼은 항상 기본 틀고정
const [frozenColumns, setFrozenColumns] = useState<string[]>(
(tableConfig.checkbox?.enabled ?? true) ? ["__checkbox__"] : []
);
const [frozenColumnCount, setFrozenColumnCount] = useState<number>(0);
// 🆕 Search Panel (통합 검색) 관련 상태
const [globalSearchTerm, setGlobalSearchTerm] = useState("");
@@ -1022,6 +1026,19 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
onColumnVisibilityChange: setColumnVisibility,
getColumnUniqueValues, // 고유 값 조회 함수 등록
onGroupSumChange: setGroupSumConfig, // 그룹별 합산 설정
// 틀고정 컬럼 관련
frozenColumnCount, // 현재 틀고정 컬럼 수
onFrozenColumnCountChange: (count: number) => {
setFrozenColumnCount(count);
// 체크박스 컬럼은 항상 틀고정에 포함
const checkboxColumn = (tableConfig.checkbox?.enabled ?? true) ? ["__checkbox__"] : [];
// 표시 가능한 컬럼 중 처음 N개를 틀고정 컬럼으로 설정
const visibleCols = columnsToRegister
.filter((col) => col.visible !== false)
.map((col) => col.columnName || col.field);
const newFrozenColumns = [...checkboxColumn, ...visibleCols.slice(0, count)];
setFrozenColumns(newFrozenColumns);
},
// 탭 관련 정보 (탭 내부의 테이블인 경우)
parentTabId,
parentTabsComponentId,
@@ -2879,6 +2896,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
sortDirection,
groupByColumns,
frozenColumns,
frozenColumnCount, // 틀고정 컬럼 수 저장
showGridLines,
headerFilters: Object.fromEntries(
Object.entries(headerFilters).map(([key, set]) => [key, Array.from(set as Set<string>)]),
@@ -2900,6 +2918,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
sortDirection,
groupByColumns,
frozenColumns,
frozenColumnCount,
showGridLines,
headerFilters,
localPageSize,
@@ -2920,7 +2939,15 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
if (state.sortColumn !== undefined) setSortColumn(state.sortColumn);
if (state.sortDirection) setSortDirection(state.sortDirection);
if (state.groupByColumns) setGroupByColumns(state.groupByColumns);
if (state.frozenColumns) setFrozenColumns(state.frozenColumns);
if (state.frozenColumns) {
// 체크박스 컬럼이 항상 포함되도록 보장
const checkboxColumn = (tableConfig.checkbox?.enabled ?? true) ? "__checkbox__" : null;
const restoredFrozenColumns = checkboxColumn && !state.frozenColumns.includes(checkboxColumn)
? [checkboxColumn, ...state.frozenColumns]
: state.frozenColumns;
setFrozenColumns(restoredFrozenColumns);
}
if (state.frozenColumnCount !== undefined) setFrozenColumnCount(state.frozenColumnCount); // 틀고정 컬럼 수 복원
if (state.showGridLines !== undefined) setShowGridLines(state.showGridLines);
if (state.headerFilters) {
const filters: Record<string, Set<string>> = {};
@@ -5590,7 +5617,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
if (isFrozen && frozenIndex > 0) {
for (let i = 0; i < frozenIndex; i++) {
const frozenCol = frozenColumns[i];
const frozenColWidth = columnWidths[frozenCol] || 150;
// 체크박스 컬럼은 48px 고정
const frozenColWidth = frozenCol === "__checkbox__" ? 48 : (columnWidths[frozenCol] || 150);
leftPosition += frozenColWidth;
}
}
@@ -5609,7 +5637,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
column.sortable !== false &&
column.columnName !== "__checkbox__" &&
"hover:bg-muted/70 cursor-pointer transition-colors",
isFrozen && "sticky z-60 shadow-[2px_0_4px_rgba(0,0,0,0.1)]",
isFrozen && "sticky z-40 shadow-[2px_0_4px_rgba(0,0,0,0.1)]",
// 🆕 Column Reordering 스타일
isColumnDragEnabled &&
column.columnName !== "__checkbox__" &&
@@ -5901,7 +5929,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
if (isFrozen && frozenIndex > 0) {
for (let i = 0; i < frozenIndex; i++) {
const frozenCol = frozenColumns[i];
const frozenColWidth = columnWidths[frozenCol] || 150;
// 체크박스 컬럼은 48px 고정
const frozenColWidth = frozenCol === "__checkbox__" ? 48 : (columnWidths[frozenCol] || 150);
leftPosition += frozenColWidth;
}
}
@@ -5914,7 +5943,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
column.columnName === "__checkbox__"
? "px-0 py-1"
: "px-2 py-1 sm:px-4 sm:py-1.5",
isFrozen && "bg-background sticky z-10 shadow-[2px_0_4px_rgba(0,0,0,0.05)]",
isFrozen && "sticky z-20 shadow-[2px_0_4px_rgba(0,0,0,0.08)]",
)}
style={{
textAlign:
@@ -5929,7 +5958,10 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
: `${100 / visibleColumns.length}%`,
minWidth: column.columnName === "__checkbox__" ? "48px" : undefined,
maxWidth: column.columnName === "__checkbox__" ? "48px" : undefined,
...(isFrozen && { left: `${leftPosition}px` }),
...(isFrozen && {
left: `${leftPosition}px`,
backgroundColor: "hsl(var(--background))",
}),
}}
>
{column.columnName === "__checkbox__"
@@ -6061,7 +6093,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
if (isFrozen && frozenIndex > 0) {
for (let i = 0; i < frozenIndex; i++) {
const frozenCol = frozenColumns[i];
const frozenColWidth = columnWidths[frozenCol] || 150;
// 체크박스 컬럼은 48px 고정
const frozenColWidth = frozenCol === "__checkbox__" ? 48 : (columnWidths[frozenCol] || 150);
leftPosition += frozenColWidth;
}
}
@@ -6074,7 +6107,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
className={cn(
"text-foreground overflow-hidden text-xs font-normal text-ellipsis whitespace-nowrap sm:text-sm",
column.columnName === "__checkbox__" ? "px-0 py-1" : "px-2 py-1 sm:px-4 sm:py-1.5",
isFrozen && "bg-background sticky z-10 shadow-[2px_0_4px_rgba(0,0,0,0.05)]",
isFrozen && "sticky z-20 shadow-[2px_0_4px_rgba(0,0,0,0.08)]",
// 🆕 포커스된 셀 스타일
isCellFocused && !editingCell && "ring-primary bg-primary/5 ring-2 ring-inset",
// 🆕 편집 중인 셀 스타일
@@ -6101,7 +6134,10 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
column.columnName === "__checkbox__" ? "48px" : `${100 / visibleColumns.length}%`,
minWidth: column.columnName === "__checkbox__" ? "48px" : undefined,
maxWidth: column.columnName === "__checkbox__" ? "48px" : undefined,
...(isFrozen && { left: `${leftPosition}px` }),
...(isFrozen && {
left: `${leftPosition}px`,
backgroundColor: "hsl(var(--background))",
}),
}}
onClick={(e) => handleCellClick(index, colIndex, e)}
onDoubleClick={() =>
@@ -6222,7 +6258,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
if (isFrozen && frozenIndex > 0) {
for (let i = 0; i < frozenIndex; i++) {
const frozenCol = frozenColumns[i];
const frozenColWidth = columnWidths[frozenCol] || 150;
// 체크박스 컬럼은 48px 고정
const frozenColWidth = frozenCol === "__checkbox__" ? 48 : (columnWidths[frozenCol] || 150);
leftPosition += frozenColWidth;
}
}
@@ -6237,7 +6274,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
className={cn(
"text-foreground text-xs font-semibold sm:text-sm",
column.columnName === "__checkbox__" ? "px-0 py-2" : "px-2 py-2 sm:px-4",
isFrozen && "bg-muted/80 sticky z-10 shadow-[2px_0_4px_rgba(0,0,0,0.05)]",
isFrozen && "sticky z-20 shadow-[2px_0_4px_rgba(0,0,0,0.08)]",
)}
style={{
textAlign: isNumeric ? "right" : column.align || "left",
@@ -6247,7 +6284,10 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
: columnWidth
? `${columnWidth}px`
: undefined,
...(isFrozen && { left: `${leftPosition}px` }),
...(isFrozen && {
left: `${leftPosition}px`,
backgroundColor: "hsl(var(--muted) / 0.8)",
}),
}}
>
{summary ? (