123
This commit is contained in:
parent
c63eaf8434
commit
b2a569f908
|
|
@ -362,15 +362,15 @@ const RealtimePreviewDynamicComponent: React.FC<RealtimePreviewProps> = ({
|
|||
// 런타임 모드에서 컴포넌트 타입별 높이 처리
|
||||
if (!isDesignMode) {
|
||||
const compType = (component as any).componentType || component.componentConfig?.type || "";
|
||||
// 테이블: 부모 flex 컨테이너가 높이 관리 (flex: 1)
|
||||
const flexGrowTypes = [
|
||||
// 레이아웃 계열: 부모 래퍼를 꽉 채움 (ResponsiveGridRenderer가 % 높이 관리)
|
||||
const fillParentTypes = [
|
||||
"table-list", "v2-table-list",
|
||||
"split-panel-layout", "split-panel-layout2",
|
||||
"v2-split-panel-layout", "screen-split-panel",
|
||||
"v2-tab-container", "tab-container",
|
||||
"tabs-widget", "v2-tabs-widget",
|
||||
];
|
||||
if (flexGrowTypes.some(t => compType === t)) {
|
||||
if (fillParentTypes.some(t => compType === t)) {
|
||||
return "100%";
|
||||
}
|
||||
const autoHeightTypes = [
|
||||
|
|
|
|||
|
|
@ -23,8 +23,9 @@ function getComponentTypeId(component: ComponentData): string {
|
|||
}
|
||||
|
||||
/**
|
||||
* 디자이너 절대좌표를 캔버스 대비 비율로 변환하여 렌더링.
|
||||
* 화면이 줄어들면 비율에 맞게 축소, 늘어나면 확대.
|
||||
* 디자이너 절대좌표를 캔버스 대비 비율(%)로 변환하여 렌더링.
|
||||
* 가로: 컨테이너 너비 대비 % → 반응형 스케일
|
||||
* 세로: 컨테이너 높이 대비 % → 뷰포트에 맞게 자동 조절
|
||||
*/
|
||||
function ProportionalRenderer({
|
||||
components,
|
||||
|
|
@ -47,19 +48,12 @@ function ProportionalRenderer({
|
|||
}, []);
|
||||
|
||||
const topLevel = components.filter((c) => !c.parentId);
|
||||
const ratio = containerW > 0 ? containerW / canvasWidth : 1;
|
||||
|
||||
const maxBottom = topLevel.reduce((max, c) => {
|
||||
const bottom = c.position.y + (c.size?.height || 40);
|
||||
return Math.max(max, bottom);
|
||||
}, 0);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
data-screen-runtime="true"
|
||||
className="bg-background relative w-full overflow-x-hidden"
|
||||
style={{ minHeight: containerW > 0 ? `${maxBottom * ratio}px` : "200px" }}
|
||||
className="bg-background relative h-full w-full overflow-hidden"
|
||||
>
|
||||
{containerW > 0 &&
|
||||
topLevel.map((component) => {
|
||||
|
|
@ -72,9 +66,9 @@ function ProportionalRenderer({
|
|||
style={{
|
||||
position: "absolute",
|
||||
left: `${(component.position.x / canvasWidth) * 100}%`,
|
||||
top: `${component.position.y * ratio}px`,
|
||||
top: `${(component.position.y / canvasHeight) * 100}%`,
|
||||
width: `${((component.size?.width || 100) / canvasWidth) * 100}%`,
|
||||
height: `${(component.size?.height || 40) * ratio}px`,
|
||||
height: `${((component.size?.height || 40) / canvasHeight) * 100}%`,
|
||||
zIndex: component.position.z || 1,
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -144,28 +144,33 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
|
|||
isDesignMode && column.hidden && "bg-muted/50 opacity-40",
|
||||
)}
|
||||
style={{
|
||||
width: getColumnWidth(column),
|
||||
minWidth: "100px", // 최소 너비 보장
|
||||
maxWidth: "300px", // 최대 너비 제한
|
||||
width: column.columnName === "__checkbox__" ? 48 : getColumnWidth(column),
|
||||
minWidth: column.columnName === "__checkbox__" ? "48px" : "100px",
|
||||
maxWidth: column.columnName === "__checkbox__" ? "48px" : "300px",
|
||||
boxSizing: "border-box",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap", // 텍스트 줄바꿈 방지
|
||||
whiteSpace: "nowrap",
|
||||
backgroundColor: "hsl(var(--muted) / 0.4)",
|
||||
// sticky 위치 설정
|
||||
...(column.fixed === "left" && { left: leftFixedWidth }),
|
||||
...(column.fixed === "right" && { right: rightFixedWidth }),
|
||||
}}
|
||||
onClick={() => column.sortable && sortHandler(column.columnName)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className={cn("flex items-center", column.columnName === "__checkbox__" ? "justify-center" : "gap-2")}>
|
||||
{column.columnName === "__checkbox__" ? (
|
||||
checkboxConfig.selectAll && (
|
||||
<Checkbox
|
||||
checked={isAllSelected}
|
||||
onCheckedChange={handleSelectAll}
|
||||
aria-label="전체 선택"
|
||||
style={{ zIndex: 1 }}
|
||||
style={{
|
||||
width: 16,
|
||||
height: 16,
|
||||
borderWidth: 1.5,
|
||||
borderColor: isAllSelected ? "hsl(var(--primary))" : "hsl(var(--muted-foreground) / 0.5)",
|
||||
zIndex: 1,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
|
|
@ -327,26 +332,22 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
|
|||
key={`cell-${column.columnName}`}
|
||||
id={isCurrentSearchResult ? "current-search-result" : undefined}
|
||||
className={cn(
|
||||
"text-foreground h-10 px-3 py-[7px] align-middle text-[11px] transition-colors",
|
||||
// 이미지 셀은 overflow/ellipsis 제외 (이미지 잘림 방지)
|
||||
"text-foreground h-10 align-middle text-[11px] transition-colors",
|
||||
column.columnName === "__checkbox__" ? "px-0 py-[7px] text-center" : "px-3 py-[7px]",
|
||||
!isReactElement && "whitespace-nowrap",
|
||||
`text-${column.align}`,
|
||||
// 고정 컬럼 스타일
|
||||
column.columnName !== "__checkbox__" && `text-${column.align}`,
|
||||
column.fixed === "left" &&
|
||||
"border-border bg-background/90 sticky z-10 border-r backdrop-blur-sm",
|
||||
column.fixed === "right" &&
|
||||
"border-border bg-background/90 sticky z-10 border-l backdrop-blur-sm",
|
||||
// 편집 가능 셀 스타일
|
||||
onCellDoubleClick && column.columnName !== "__checkbox__" && "cursor-text",
|
||||
)}
|
||||
style={{
|
||||
width: getColumnWidth(column),
|
||||
minWidth: "100px", // 최소 너비 보장
|
||||
maxWidth: "300px", // 최대 너비 제한
|
||||
width: column.columnName === "__checkbox__" ? 48 : getColumnWidth(column),
|
||||
minWidth: column.columnName === "__checkbox__" ? "48px" : "100px",
|
||||
maxWidth: column.columnName === "__checkbox__" ? "48px" : "300px",
|
||||
boxSizing: "border-box",
|
||||
// 이미지 셀은 overflow 허용
|
||||
...(isReactElement ? {} : { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }),
|
||||
// sticky 위치 설정
|
||||
...(column.fixed === "left" && { left: leftFixedWidth }),
|
||||
...(column.fixed === "right" && { right: rightFixedWidth }),
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -878,10 +878,7 @@ 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[]>(
|
||||
(tableConfig.checkbox?.enabled ?? true) ? ["__checkbox__"] : [],
|
||||
);
|
||||
const [frozenColumns, setFrozenColumns] = useState<string[]>([]);
|
||||
const [frozenColumnCount, setFrozenColumnCount] = useState<number>(0);
|
||||
|
||||
// 🆕 Search Panel (통합 검색) 관련 상태
|
||||
|
|
@ -1173,14 +1170,10 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
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);
|
||||
setFrozenColumns(visibleCols.slice(0, count));
|
||||
},
|
||||
// 탭 관련 정보 (탭 내부의 테이블인 경우)
|
||||
parentTabId,
|
||||
|
|
@ -3080,11 +3073,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
if (state.groupByColumns) setGroupByColumns(state.groupByColumns);
|
||||
if (state.frozenColumns) {
|
||||
// 체크박스 컬럼이 항상 포함되도록 보장
|
||||
const checkboxColumn = (tableConfig.checkbox?.enabled ?? true) ? "__checkbox__" : null;
|
||||
const restoredFrozenColumns =
|
||||
checkboxColumn && !state.frozenColumns.includes(checkboxColumn)
|
||||
? [checkboxColumn, ...state.frozenColumns]
|
||||
: state.frozenColumns;
|
||||
const restoredFrozenColumns = (state.frozenColumns || []).filter((col: string) => col !== "__checkbox__");
|
||||
setFrozenColumns(restoredFrozenColumns);
|
||||
}
|
||||
if (state.frozenColumnCount !== undefined) setFrozenColumnCount(state.frozenColumnCount); // 틀고정 컬럼 수 복원
|
||||
|
|
@ -4233,7 +4222,19 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
if (!tableConfig.checkbox?.selectAll) return null;
|
||||
if (tableConfig.checkbox?.multiple === false) return null;
|
||||
|
||||
return <Checkbox checked={isAllSelected} onCheckedChange={handleSelectAll} aria-label="전체 선택" />;
|
||||
return (
|
||||
<Checkbox
|
||||
checked={isAllSelected}
|
||||
onCheckedChange={handleSelectAll}
|
||||
aria-label="전체 선택"
|
||||
style={{
|
||||
width: 16,
|
||||
height: 16,
|
||||
borderWidth: 1.5,
|
||||
borderColor: isAllSelected ? "hsl(var(--primary))" : "hsl(var(--muted-foreground) / 0.5)",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const renderCheckboxCell = (row: any, index: number) => {
|
||||
|
|
@ -4245,6 +4246,12 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
checked={isChecked}
|
||||
onCheckedChange={(checked) => handleRowSelection(rowKey, checked as boolean)}
|
||||
aria-label={`행 ${index + 1} 선택`}
|
||||
style={{
|
||||
width: 16,
|
||||
height: 16,
|
||||
borderWidth: 1.5,
|
||||
borderColor: isChecked ? "hsl(var(--primary))" : "hsl(var(--muted-foreground) / 0.5)",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
@ -6201,7 +6208,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
"text-foreground text-[11px] font-normal",
|
||||
inputType !== "image" && "overflow-hidden text-ellipsis whitespace-nowrap max-w-[170px]",
|
||||
column.columnName === "__checkbox__"
|
||||
? "px-0 py-[7px]"
|
||||
? "px-0 py-[7px] text-center"
|
||||
: "px-3 py-[7px]",
|
||||
isFrozen && "sticky z-20 shadow-[2px_0_4px_rgba(0,0,0,0.08)]",
|
||||
(inputType === "code" || inputType === "category") && "font-mono text-[10px] text-primary font-medium",
|
||||
|
|
@ -6370,7 +6377,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
className={cn(
|
||||
"text-foreground text-[11px] font-normal",
|
||||
inputType !== "image" && "overflow-hidden text-ellipsis whitespace-nowrap max-w-[170px]",
|
||||
column.columnName === "__checkbox__" ? "px-0 py-[7px]" : "px-3 py-[7px]",
|
||||
column.columnName === "__checkbox__" ? "px-0 py-[7px] text-center" : "px-3 py-[7px]",
|
||||
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",
|
||||
editingCell?.rowIndex === index && editingCell?.colIndex === colIndex && "p-0",
|
||||
|
|
|
|||
Loading…
Reference in New Issue