diff --git a/.gitignore b/.gitignore index 5e66bd12..552d1265 100644 --- a/.gitignore +++ b/.gitignore @@ -153,6 +153,7 @@ backend-node/uploads/ uploads/ *.jpg *.jpeg +*.png *.gif *.pdf *.doc diff --git a/.playwright-mcp/pivotgrid-demo.png b/.playwright-mcp/pivotgrid-demo.png deleted file mode 100644 index 0fad6fa6..00000000 Binary files a/.playwright-mcp/pivotgrid-demo.png and /dev/null differ diff --git a/.playwright-mcp/pivotgrid-table.png b/.playwright-mcp/pivotgrid-table.png deleted file mode 100644 index 79041f47..00000000 Binary files a/.playwright-mcp/pivotgrid-table.png and /dev/null differ diff --git a/.playwright-mcp/pop-page-initial.png b/.playwright-mcp/pop-page-initial.png deleted file mode 100644 index b14666b3..00000000 Binary files a/.playwright-mcp/pop-page-initial.png and /dev/null differ diff --git a/frontend/app/globals.css b/frontend/app/globals.css index b3dbab89..b9173da6 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -402,18 +402,9 @@ select { /* 필요시 특정 컴포넌트에 대한 스타일 오버라이드를 여기에 추가 */ /* 예: Calendar, Table 등의 미세 조정 */ -/* 모바일에서 테이블 레이아웃 고정 (화면 밖으로 넘어가지 않도록) */ -@media (max-width: 639px) { - .table-mobile-fixed { - table-layout: fixed; - } -} - -/* 데스크톱에서 테이블 레이아웃 자동 (기본값이지만 명시적으로 설정) */ -@media (min-width: 640px) { - .table-mobile-fixed { - table-layout: auto; - } +/* 테이블 레이아웃 고정 (셀 내용이 영역을 벗어나지 않도록) */ +.table-mobile-fixed { + table-layout: fixed; } /* 그리드선 숨기기 */ diff --git a/frontend/components/screen/RealtimePreviewDynamic.tsx b/frontend/components/screen/RealtimePreviewDynamic.tsx index 4726cf39..d23337b5 100644 --- a/frontend/components/screen/RealtimePreviewDynamic.tsx +++ b/frontend/components/screen/RealtimePreviewDynamic.tsx @@ -583,7 +583,7 @@ const RealtimePreviewDynamicComponent: React.FC = ({ const needsStripBorder = isV2HorizLabel || isButtonComponent; const safeComponentStyle = needsStripBorder ? (() => { - const { borderWidth, borderColor, borderStyle, border, borderRadius, ...rest } = componentStyle as any; + const { borderWidth, borderColor, borderStyle, border, ...rest } = componentStyle as any; return rest; })() : componentStyle; diff --git a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx index b5e8852f..597c759a 100644 --- a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx +++ b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx @@ -2670,7 +2670,7 @@ export const SplitPanelLayoutComponent: React.FC {formatCellValue( col.name, @@ -2732,7 +2732,7 @@ export const SplitPanelLayoutComponent: React.FC {formatCellValue( col.name, @@ -3415,7 +3415,7 @@ export const SplitPanelLayoutComponent: React.FC {formatCellValue( col.name, diff --git a/frontend/lib/registry/components/table-list/TableListComponent.tsx b/frontend/lib/registry/components/table-list/TableListComponent.tsx index babec53c..119cca53 100644 --- a/frontend/lib/registry/components/table-list/TableListComponent.tsx +++ b/frontend/lib/registry/components/table-list/TableListComponent.tsx @@ -379,12 +379,33 @@ export const TableListComponent: React.FC = ({ } }, [tableConfig.selectedTable, currentUserId]); - // columnVisibility 변경 시 컬럼 순서 및 가시성 적용 + // columnVisibility 변경 시 컬럼 순서, 가시성, 너비 적용 useEffect(() => { if (columnVisibility.length > 0) { const newOrder = columnVisibility.map((cv) => cv.columnName).filter((name) => name !== "__checkbox__"); // 체크박스 제외 setColumnOrder(newOrder); + // 너비 적용 + const newWidths: Record = {}; + columnVisibility.forEach((cv) => { + if (cv.width) { + newWidths[cv.columnName] = cv.width; + } + }); + if (Object.keys(newWidths).length > 0) { + setColumnWidths((prev) => ({ ...prev, ...newWidths })); + + // table_column_widths_* localStorage도 동기화 (초기 너비 로드 시 올바른 값 사용) + if (tableConfig.selectedTable && userId) { + const widthsKey = `table_column_widths_${tableConfig.selectedTable}_${userId}`; + try { + const existing = localStorage.getItem(widthsKey); + const merged = existing ? { ...JSON.parse(existing), ...newWidths } : newWidths; + localStorage.setItem(widthsKey, JSON.stringify(merged)); + } catch { /* ignore */ } + } + } + // localStorage에 저장 (사용자별) if (tableConfig.selectedTable && currentUserId) { const storageKey = `table_column_visibility_${tableConfig.selectedTable}_${currentUserId}`; diff --git a/frontend/lib/registry/components/v2-button-primary/ButtonPrimaryComponent.tsx b/frontend/lib/registry/components/v2-button-primary/ButtonPrimaryComponent.tsx index fa0cfaae..e287d512 100644 --- a/frontend/lib/registry/components/v2-button-primary/ButtonPrimaryComponent.tsx +++ b/frontend/lib/registry/components/v2-button-primary/ButtonPrimaryComponent.tsx @@ -570,6 +570,8 @@ export const ButtonPrimaryComponent: React.FC = ({ ...restComponentStyle, width: "100%", height: "100%", + borderRadius: _br || "0.5rem", + overflow: "hidden", }; // 디자인 모드 스타일 diff --git a/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx b/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx index 97ffef4e..c89fb1d3 100644 --- a/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx +++ b/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx @@ -3607,7 +3607,7 @@ export const SplitPanelLayoutComponent: React.FC {formatCellValue( col.name, @@ -3704,7 +3704,7 @@ export const SplitPanelLayoutComponent: React.FC {formatCellValue( col.name, @@ -4201,7 +4201,7 @@ export const SplitPanelLayoutComponent: React.FC onClick={() => toggleRightItemExpansion(`tab_${activeTabIndex}_${tabItemId}`)} > {tabSummaryColumns.map((col: any) => ( - + {col.type === "progress" ? renderProgressCell(col, item, selectedLeftItem) : formatCellValue( @@ -4317,7 +4317,7 @@ export const SplitPanelLayoutComponent: React.FC onClick={() => toggleRightItemExpansion(`tab_${activeTabIndex}_${tabItemId}`)} > {listSummaryColumns.map((col: any) => ( - + {col.type === "progress" ? renderProgressCell(col, item, selectedLeftItem) : formatCellValue( @@ -4709,8 +4709,8 @@ export const SplitPanelLayoutComponent: React.FC {columnsToShow.map((col, colIdx) => ( {col.type === "progress" ? renderProgressCell(col, item, selectedLeftItem) @@ -4856,7 +4856,7 @@ export const SplitPanelLayoutComponent: React.FC onClick={() => toggleRightItemExpansion(itemId)} > {columnsToDisplay.map((col) => ( - + {formatCellValue( col.name, getEntityJoinValue(item, col.name), diff --git a/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx b/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx index 65833fa9..15b7a13b 100644 --- a/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx +++ b/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx @@ -521,12 +521,33 @@ export const TableListComponent: React.FC = ({ } }, [tableConfig.selectedTable, currentUserId]); - // columnVisibility 변경 시 컬럼 순서 및 가시성 적용 + // columnVisibility 변경 시 컬럼 순서, 가시성, 너비 적용 useEffect(() => { if (columnVisibility.length > 0) { const newOrder = columnVisibility.map((cv) => cv.columnName).filter((name) => name !== "__checkbox__"); // 체크박스 제외 setColumnOrder(newOrder); + // 너비 적용 + const newWidths: Record = {}; + columnVisibility.forEach((cv) => { + if (cv.width) { + newWidths[cv.columnName] = cv.width; + } + }); + if (Object.keys(newWidths).length > 0) { + setColumnWidths((prev) => ({ ...prev, ...newWidths })); + + // table_column_widths_* localStorage도 동기화 (초기 너비 로드 시 올바른 값 사용) + if (tableConfig.selectedTable && userId) { + const widthsKey = `table_column_widths_${tableConfig.selectedTable}_${userId}`; + try { + const existing = localStorage.getItem(widthsKey); + const merged = existing ? { ...JSON.parse(existing), ...newWidths } : newWidths; + localStorage.setItem(widthsKey, JSON.stringify(merged)); + } catch { /* ignore */ } + } + } + // localStorage에 저장 (사용자별) if (tableConfig.selectedTable && currentUserId) { const storageKey = `table_column_visibility_${tableConfig.selectedTable}_${currentUserId}`;