feature/v2-unified-renewal #379
|
|
@ -489,8 +489,73 @@ function ScreenViewPage() {
|
||||||
(c as any).componentType === "conditional-container",
|
(c as any).componentType === "conditional-container",
|
||||||
);
|
);
|
||||||
|
|
||||||
// TableSearchWidget 및 조건부 컨테이너 높이 차이를 계산하여 Y 위치 조정
|
// 🆕 같은 X 영역(섹션)에서 컴포넌트들이 겹치지 않도록 자동 수직 정렬
|
||||||
const adjustedComponents = regularComponents.map((component) => {
|
const autoLayoutComponents = (() => {
|
||||||
|
// X 위치 기준으로 섹션 그룹화 (50px 오차 범위)
|
||||||
|
const X_THRESHOLD = 50;
|
||||||
|
const GAP = 16; // 컴포넌트 간 간격
|
||||||
|
|
||||||
|
// 컴포넌트를 X 섹션별로 그룹화
|
||||||
|
const sections: Map<number, typeof regularComponents> = new Map();
|
||||||
|
|
||||||
|
regularComponents.forEach((comp) => {
|
||||||
|
const x = comp.position.x;
|
||||||
|
let foundSection = false;
|
||||||
|
|
||||||
|
for (const [sectionX, components] of sections.entries()) {
|
||||||
|
if (Math.abs(x - sectionX) < X_THRESHOLD) {
|
||||||
|
components.push(comp);
|
||||||
|
foundSection = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundSection) {
|
||||||
|
sections.set(x, [comp]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 각 섹션 내에서 Y 위치 순으로 정렬 후 자동 배치
|
||||||
|
const adjustedMap = new Map<string, typeof regularComponents[0]>();
|
||||||
|
|
||||||
|
for (const [sectionX, components] of sections.entries()) {
|
||||||
|
// 섹션 내 2개 이상 컴포넌트가 있을 때만 자동 배치
|
||||||
|
if (components.length >= 2) {
|
||||||
|
// Y 위치 순으로 정렬
|
||||||
|
const sorted = [...components].sort((a, b) => a.position.y - b.position.y);
|
||||||
|
|
||||||
|
let currentY = sorted[0].position.y;
|
||||||
|
|
||||||
|
sorted.forEach((comp, index) => {
|
||||||
|
if (index === 0) {
|
||||||
|
adjustedMap.set(comp.id, comp);
|
||||||
|
} else {
|
||||||
|
// 이전 컴포넌트 아래로 배치
|
||||||
|
const prevComp = sorted[index - 1];
|
||||||
|
const prevAdjusted = adjustedMap.get(prevComp.id) || prevComp;
|
||||||
|
const prevBottom = prevAdjusted.position.y + (prevAdjusted.size?.height || 100);
|
||||||
|
const newY = prevBottom + GAP;
|
||||||
|
|
||||||
|
adjustedMap.set(comp.id, {
|
||||||
|
...comp,
|
||||||
|
position: {
|
||||||
|
...comp.position,
|
||||||
|
y: newY,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 단일 컴포넌트는 그대로
|
||||||
|
components.forEach((comp) => adjustedMap.set(comp.id, comp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return regularComponents.map((comp) => adjustedMap.get(comp.id) || comp);
|
||||||
|
})();
|
||||||
|
|
||||||
|
// TableSearchWidget 및 조건부 컨테이너 높이 차이를 계산하여 Y 위치 추가 조정
|
||||||
|
const adjustedComponents = autoLayoutComponents.map((component) => {
|
||||||
const isTableSearchWidget = (component as any).componentId === "table-search-widget";
|
const isTableSearchWidget = (component as any).componentId === "table-search-widget";
|
||||||
const isConditionalContainer = (component as any).componentId === "conditional-container";
|
const isConditionalContainer = (component as any).componentId === "conditional-container";
|
||||||
|
|
||||||
|
|
@ -511,30 +576,15 @@ function ScreenViewPage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🆕 조건부 컨테이너 높이 조정
|
// 조건부 컨테이너 높이 조정
|
||||||
for (const container of conditionalContainers) {
|
for (const container of conditionalContainers) {
|
||||||
const isBelow = component.position.y > container.position.y;
|
const isBelow = component.position.y > container.position.y;
|
||||||
const actualHeight = conditionalContainerHeights[container.id];
|
const actualHeight = conditionalContainerHeights[container.id];
|
||||||
const originalHeight = container.size?.height || 200;
|
const originalHeight = container.size?.height || 200;
|
||||||
const heightDiff = actualHeight ? actualHeight - originalHeight : 0;
|
const heightDiff = actualHeight ? actualHeight - originalHeight : 0;
|
||||||
|
|
||||||
console.log(`🔍 높이 조정 체크:`, {
|
|
||||||
componentId: component.id,
|
|
||||||
componentY: component.position.y,
|
|
||||||
containerY: container.position.y,
|
|
||||||
isBelow,
|
|
||||||
actualHeight,
|
|
||||||
originalHeight,
|
|
||||||
heightDiff,
|
|
||||||
containerId: container.id,
|
|
||||||
containerSize: container.size,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isBelow && heightDiff > 0) {
|
if (isBelow && heightDiff > 0) {
|
||||||
totalHeightAdjustment += heightDiff;
|
totalHeightAdjustment += heightDiff;
|
||||||
console.log(
|
|
||||||
`📐 컴포넌트 ${component.id} 위치 조정: ${heightDiff}px (조건부 컨테이너 ${container.id})`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -307,12 +307,15 @@ export function RepeatContainerComponent({
|
||||||
return {
|
return {
|
||||||
minWidth: itemMinWidth,
|
minWidth: itemMinWidth,
|
||||||
maxWidth: itemMaxWidth,
|
maxWidth: itemMaxWidth,
|
||||||
height: itemHeight,
|
// height 대신 minHeight 사용 - 내부 컨텐츠가 커지면 자동으로 높이 확장
|
||||||
|
minHeight: itemHeight || "auto",
|
||||||
|
height: "auto", // 고정 높이 대신 auto로 변경
|
||||||
backgroundColor: backgroundColor || "#ffffff",
|
backgroundColor: backgroundColor || "#ffffff",
|
||||||
borderRadius: borderRadius || "8px",
|
borderRadius: borderRadius || "8px",
|
||||||
padding: padding || "16px",
|
padding: padding || "16px",
|
||||||
border: showBorder ? "1px solid #e5e7eb" : "none",
|
border: showBorder ? "1px solid #e5e7eb" : "none",
|
||||||
boxShadow: showShadow ? "0 1px 3px rgba(0,0,0,0.1)" : "none",
|
boxShadow: showShadow ? "0 1px 3px rgba(0,0,0,0.1)" : "none",
|
||||||
|
overflow: "visible", // 내부 컨텐츠가 튀어나가지 않도록
|
||||||
};
|
};
|
||||||
}, [itemMinWidth, itemMaxWidth, itemHeight, backgroundColor, borderRadius, padding, showBorder, showShadow]);
|
}, [itemMinWidth, itemMaxWidth, itemHeight, backgroundColor, borderRadius, padding, showBorder, showShadow]);
|
||||||
|
|
||||||
|
|
@ -343,11 +346,11 @@ export function RepeatContainerComponent({
|
||||||
_isLast: context.isLast,
|
_isLast: context.isLast,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 슬롯에 배치된 컴포넌트들을 렌더링
|
// 슬롯에 배치된 컴포넌트들을 렌더링 (Flow 레이아웃으로 변경)
|
||||||
return (
|
return (
|
||||||
<div className="relative" style={{ minHeight: "50px" }}>
|
<div className="flex flex-col gap-3">
|
||||||
{slotChildren.map((childComp: SlotComponentConfig) => {
|
{slotChildren.map((childComp: SlotComponentConfig) => {
|
||||||
const { position = { x: 0, y: 0 }, size = { width: 200, height: 40 } } = childComp;
|
const { size = { width: "100%", height: "auto" } } = childComp;
|
||||||
|
|
||||||
// DynamicComponentRenderer가 기대하는 형식으로 변환
|
// DynamicComponentRenderer가 기대하는 형식으로 변환
|
||||||
const componentData = {
|
const componentData = {
|
||||||
|
|
@ -355,8 +358,11 @@ export function RepeatContainerComponent({
|
||||||
componentType: childComp.componentType,
|
componentType: childComp.componentType,
|
||||||
label: childComp.label,
|
label: childComp.label,
|
||||||
columnName: childComp.fieldName,
|
columnName: childComp.fieldName,
|
||||||
position: { ...position, z: 1 },
|
position: { x: 0, y: 0, z: 1 },
|
||||||
size,
|
size: {
|
||||||
|
width: typeof size.width === "number" ? size.width : undefined,
|
||||||
|
height: typeof size.height === "number" ? size.height : undefined,
|
||||||
|
},
|
||||||
componentConfig: childComp.componentConfig,
|
componentConfig: childComp.componentConfig,
|
||||||
style: childComp.style,
|
style: childComp.style,
|
||||||
};
|
};
|
||||||
|
|
@ -364,12 +370,10 @@ export function RepeatContainerComponent({
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={componentData.id}
|
key={componentData.id}
|
||||||
className="absolute"
|
className="w-full"
|
||||||
style={{
|
style={{
|
||||||
left: position.x || 0,
|
// 너비는 100%로, 높이는 자동으로
|
||||||
top: position.y || 0,
|
minHeight: typeof size.height === "number" ? size.height : "auto",
|
||||||
width: size.width || 200,
|
|
||||||
height: size.height || 40,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DynamicComponentRenderer
|
<DynamicComponentRenderer
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue