스타일 적용안되던 문제 수정

This commit is contained in:
kjs
2025-09-04 11:33:52 +09:00
parent bc23f64b42
commit feb26fa32a
11 changed files with 1767 additions and 248 deletions

View File

@@ -633,7 +633,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 격자 스냅 적용
const snappedPosition =
layout.gridSettings?.snapToGrid && gridInfo
? snapToGrid({ x: dropX, y: dropY, z: 1 }, gridInfo, layout.gridSettings)
? snapToGrid({ x: dropX, y: dropY, z: 1 }, gridInfo, layout.gridSettings as GridUtilSettings)
: { x: dropX, y: dropY, z: 1 };
console.log("🎨 템플릿 드롭:", {
@@ -644,8 +644,19 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
});
// 템플릿의 모든 컴포넌트들을 생성
// 먼저 ID 매핑을 생성 (parentId 참조를 위해)
const idMapping: Record<string, string> = {};
template.components.forEach((templateComp, index) => {
const newId = generateComponentId();
if (index === 0) {
// 첫 번째 컴포넌트(컨테이너)는 "form-container"로 매핑
idMapping["form-container"] = newId;
}
idMapping[templateComp.parentId || `temp_${index}`] = newId;
});
const newComponents: ComponentData[] = template.components.map((templateComp, index) => {
const componentId = generateComponentId();
const componentId = index === 0 ? idMapping["form-container"] : generateComponentId();
// 템플릿 컴포넌트의 상대 위치를 드롭 위치 기준으로 조정
const absoluteX = snappedPosition.x + templateComp.position.x;
@@ -654,17 +665,38 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 격자 스냅 적용
const finalPosition =
layout.gridSettings?.snapToGrid && gridInfo
? snapToGrid({ x: absoluteX, y: absoluteY, z: 1 }, gridInfo, layout.gridSettings)
? snapToGrid({ x: absoluteX, y: absoluteY, z: 1 }, gridInfo, layout.gridSettings as GridUtilSettings)
: { x: absoluteX, y: absoluteY, z: 1 };
if (templateComp.type === "container") {
// 그리드 컬럼 기반 크기 계산
const gridColumns =
typeof templateComp.size.width === "number" && templateComp.size.width <= 12 ? templateComp.size.width : 4; // 기본 4컬럼
const calculatedSize =
gridInfo && layout.gridSettings?.snapToGrid
? (() => {
const newWidth = calculateWidthFromColumns(
gridColumns,
gridInfo,
layout.gridSettings as GridUtilSettings,
);
return {
width: newWidth,
height: templateComp.size.height,
};
})()
: { width: 400, height: templateComp.size.height }; // 폴백 크기
return {
id: componentId,
type: "container",
label: templateComp.label,
tableName: selectedScreen?.tableName || "",
title: templateComp.title || templateComp.label,
position: finalPosition,
size: templateComp.size,
size: calculatedSize,
gridColumns,
style: {
labelDisplay: true,
labelFontSize: "14px",
@@ -817,6 +849,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
label: templateComp.label,
placeholder: templateComp.placeholder,
columnName: `field_${index + 1}`,
parentId: templateComp.parentId ? idMapping[templateComp.parentId] : undefined,
position: finalPosition,
size: templateComp.size,
required: templateComp.required || false,
@@ -878,6 +911,11 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
// 기존 테이블/컬럼 드래그 처리
const { type, table, column } = parsedData;
// 드롭 대상이 폼 컨테이너인지 확인
const dropTarget = e.target as HTMLElement;
const formContainer = dropTarget.closest('[data-form-container="true"]');
const rect = canvasRef.current?.getBoundingClientRect();
if (!rect) return;
@@ -1031,29 +1069,65 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
}
};
// 컬럼 위젯 생성
newComponent = {
id: generateComponentId(),
type: "widget",
label: column.columnName,
tableName: table.tableName,
columnName: column.columnName,
widgetType: column.widgetType,
// dataType: column.dataType, // WidgetComponent에 dataType 속성이 없음
required: column.required,
readonly: false, // 누락된 속성 추가
position: { x, y, z: 1 } as Position,
size: { width: columnWidth, height: 40 },
gridColumns: 1, // 기본 그리드 컬럼 수
style: {
labelDisplay: true,
labelFontSize: "12px",
labelColor: "#374151",
labelFontWeight: "500",
labelMarginBottom: "6px",
},
webTypeConfig: getDefaultWebTypeConfig(column.widgetType),
};
// 폼 컨테이너에 드롭한 경우
if (formContainer) {
const formContainerId = formContainer.getAttribute("data-component-id");
const formContainerComponent = layout.components.find((c) => c.id === formContainerId);
if (formContainerComponent) {
// 폼 내부에서의 상대적 위치 계산
const containerRect = formContainer.getBoundingClientRect();
const relativeX = e.clientX - containerRect.left;
const relativeY = e.clientY - containerRect.top;
newComponent = {
id: generateComponentId(),
type: "widget",
label: column.columnName,
tableName: table.tableName,
columnName: column.columnName,
widgetType: column.widgetType,
required: column.required,
readonly: false,
parentId: formContainerId, // 폼 컨테이너의 자식으로 설정
position: { x: relativeX, y: relativeY, z: 1 } as Position,
size: { width: 200, height: 40 },
style: {
labelDisplay: true,
labelFontSize: "12px",
labelColor: "#374151",
labelFontWeight: "500",
labelMarginBottom: "6px",
},
webTypeConfig: getDefaultWebTypeConfig(column.widgetType),
};
} else {
return; // 폼 컨테이너를 찾을 수 없으면 드롭 취소
}
} else {
// 일반 캔버스에 드롭한 경우 (기존 로직)
newComponent = {
id: generateComponentId(),
type: "widget",
label: column.columnName,
tableName: table.tableName,
columnName: column.columnName,
widgetType: column.widgetType,
required: column.required,
readonly: false,
position: { x, y, z: 1 } as Position,
size: { width: columnWidth, height: 40 },
gridColumns: 1,
style: {
labelDisplay: true,
labelFontSize: "12px",
labelColor: "#374151",
labelFontWeight: "500",
labelMarginBottom: "6px",
},
webTypeConfig: getDefaultWebTypeConfig(column.widgetType),
};
}
} else {
return;
}
@@ -2273,79 +2347,106 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
}
return (
<RealtimePreview
key={`${component.id}-${component.type === "widget" ? JSON.stringify((component as any).webTypeConfig) : ""}`}
component={displayComponent}
isSelected={
selectedComponent?.id === component.id || groupState.selectedComponents.includes(component.id)
}
onClick={(e) => handleComponentClick(component, e)}
onDragStart={(e) => startComponentDrag(component, e)}
onDragEnd={endDrag}
<div
key={component.id}
className="absolute"
style={{
left: `${displayComponent.position.x}px`,
top: `${displayComponent.position.y}px`,
width: displayComponent.style?.width || `${displayComponent.size.width}px`,
height: displayComponent.style?.height || `${displayComponent.size.height}px`,
zIndex: displayComponent.position.z || 1,
}}
>
{children.map((child) => {
// 자식 컴포넌트에도 드래그 피드백 적용
const isChildDraggingThis = dragState.isDragging && dragState.draggedComponent?.id === child.id;
const isChildBeingDragged =
dragState.isDragging && dragState.draggedComponents.some((dragComp) => dragComp.id === child.id);
let displayChild = child;
if (isChildBeingDragged) {
if (isChildDraggingThis) {
// 주 드래그 자식 컴포넌트
displayChild = {
...child,
position: dragState.currentPosition,
style: {
...child.style,
opacity: 0.8,
transform: "scale(1.02)",
transition: "none",
zIndex: 9999,
},
};
} else {
// 다른 선택된 자식 컴포넌트들
const originalChildComponent = dragState.draggedComponents.find(
(dragComp) => dragComp.id === child.id,
);
if (originalChildComponent) {
const deltaX = dragState.currentPosition.x - dragState.originalPosition.x;
const deltaY = dragState.currentPosition.y - dragState.originalPosition.y;
displayChild = {
...child,
position: {
x: originalChildComponent.position.x + deltaX,
y: originalChildComponent.position.y + deltaY,
z: originalChildComponent.position.z || 1,
} as Position,
style: {
...child.style,
opacity: 0.8,
transition: "none",
zIndex: 8888,
},
};
}
}
<RealtimePreview
component={displayComponent}
isSelected={
selectedComponent?.id === component.id || groupState.selectedComponents.includes(component.id)
}
onClick={(e) => handleComponentClick(component, e)}
onDragStart={(e) => startComponentDrag(component, e)}
onDragEnd={endDrag}
>
{/* 컨테이너 및 그룹의 자식 컴포넌트들 렌더링 */}
{(component.type === "group" || component.type === "container") &&
layout.components
.filter((child) => child.parentId === component.id)
.map((child) => {
// 자식 컴포넌트에도 드래그 피드백 적용
const isChildDraggingThis = dragState.isDragging && dragState.draggedComponent?.id === child.id;
const isChildBeingDragged =
dragState.isDragging &&
dragState.draggedComponents.some((dragComp) => dragComp.id === child.id);
return (
<RealtimePreview
key={`${child.id}-${child.type === "widget" ? JSON.stringify((child as any).webTypeConfig) : ""}`}
component={displayChild}
isSelected={
selectedComponent?.id === child.id || groupState.selectedComponents.includes(child.id)
}
onClick={(e) => handleComponentClick(child, e)}
onDragStart={(e) => startComponentDrag(child, e)}
onDragEnd={endDrag}
/>
);
})}
</RealtimePreview>
let displayChild = child;
if (isChildBeingDragged) {
if (isChildDraggingThis) {
// 주 드래그 자식 컴포넌트
displayChild = {
...child,
position: dragState.currentPosition,
style: {
...child.style,
opacity: 0.8,
transform: "scale(1.02)",
transition: "none",
zIndex: 9999,
},
};
} else {
// 다른 선택된 자식 컴포넌트들
const originalChildComponent = dragState.draggedComponents.find(
(dragComp) => dragComp.id === child.id,
);
if (originalChildComponent) {
const deltaX = dragState.currentPosition.x - dragState.originalPosition.x;
const deltaY = dragState.currentPosition.y - dragState.originalPosition.y;
displayChild = {
...child,
position: {
x: originalChildComponent.position.x + deltaX,
y: originalChildComponent.position.y + deltaY,
z: originalChildComponent.position.z || 1,
} as Position,
style: {
...child.style,
opacity: 0.8,
transition: "none",
zIndex: 8888,
},
};
}
}
}
return (
<div
key={child.id}
className="absolute"
style={{
left: `${displayChild.position.x - component.position.x}px`,
top: `${displayChild.position.y - component.position.y}px`,
width: `${displayChild.size.width}px`,
height: `${displayChild.size.height}px`, // 순수 컴포넌트 높이만 사용
zIndex: displayChild.position.z || 1,
}}
>
<RealtimePreview
component={displayChild}
isSelected={
selectedComponent?.id === child.id || groupState.selectedComponents.includes(child.id)
}
onClick={(e) => handleComponentClick(child, e)}
onDragStart={(e) => startComponentDrag(child, e)}
onDragEnd={endDrag}
/>
</div>
);
})}
</RealtimePreview>
</div>
);
})}
@@ -2475,7 +2576,40 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
<div className="p-4">
<StyleEditor
style={selectedComponent.style || {}}
onStyleChange={(newStyle) => updateComponentProperty(selectedComponent.id, "style", newStyle)}
onStyleChange={(newStyle) => {
console.log("🔧 StyleEditor 크기 변경:", {
componentId: selectedComponent.id,
newStyle,
currentSize: selectedComponent.size,
hasWidth: !!newStyle.width,
hasHeight: !!newStyle.height,
});
// 스타일 업데이트
updateComponentProperty(selectedComponent.id, "style", newStyle);
// 크기가 변경된 경우 component.size도 업데이트
if (newStyle.width || newStyle.height) {
const width = newStyle.width
? parseInt(newStyle.width.replace("px", ""))
: selectedComponent.size.width;
const height = newStyle.height
? parseInt(newStyle.height.replace("px", ""))
: selectedComponent.size.height;
console.log("📏 크기 업데이트:", {
originalWidth: selectedComponent.size.width,
originalHeight: selectedComponent.size.height,
newWidth: width,
newHeight: height,
styleWidth: newStyle.width,
styleHeight: newStyle.height,
});
updateComponentProperty(selectedComponent.id, "size.width", width);
updateComponentProperty(selectedComponent.id, "size.height", height);
}
}}
/>
</div>
) : (