버튼 수정
This commit is contained in:
parent
7aecae559b
commit
d64ca5a8c0
|
|
@ -26,7 +26,7 @@ export default function ScreenViewPage() {
|
||||||
|
|
||||||
// 🆕 현재 로그인한 사용자 정보
|
// 🆕 현재 로그인한 사용자 정보
|
||||||
const { user, userName, companyCode } = useAuth();
|
const { user, userName, companyCode } = useAuth();
|
||||||
|
|
||||||
// 🆕 모바일 환경 감지
|
// 🆕 모바일 환경 감지
|
||||||
const { isMobile } = useResponsive();
|
const { isMobile } = useResponsive();
|
||||||
|
|
||||||
|
|
@ -189,10 +189,10 @@ export default function ScreenViewPage() {
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full min-h-[400px] w-full items-center justify-center bg-gradient-to-br from-muted to-muted/50">
|
<div className="from-muted to-muted/50 flex h-full min-h-[400px] w-full items-center justify-center bg-gradient-to-br">
|
||||||
<div className="rounded-xl border border-border bg-background p-8 text-center shadow-lg">
|
<div className="border-border bg-background rounded-xl border p-8 text-center shadow-lg">
|
||||||
<Loader2 className="mx-auto h-10 w-10 animate-spin text-primary" />
|
<Loader2 className="text-primary mx-auto h-10 w-10 animate-spin" />
|
||||||
<p className="mt-4 font-medium text-foreground">화면을 불러오는 중...</p>
|
<p className="text-foreground mt-4 font-medium">화면을 불러오는 중...</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -200,13 +200,13 @@ export default function ScreenViewPage() {
|
||||||
|
|
||||||
if (error || !screen) {
|
if (error || !screen) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full min-h-[400px] w-full items-center justify-center bg-gradient-to-br from-muted to-muted/50">
|
<div className="from-muted to-muted/50 flex h-full min-h-[400px] w-full items-center justify-center bg-gradient-to-br">
|
||||||
<div className="max-w-md rounded-xl border border-border bg-background p-8 text-center shadow-lg">
|
<div className="border-border bg-background max-w-md rounded-xl border p-8 text-center shadow-lg">
|
||||||
<div className="mx-auto mb-6 flex h-20 w-20 items-center justify-center rounded-full bg-gradient-to-br from-destructive/20 to-warning/20 shadow-sm">
|
<div className="from-destructive/20 to-warning/20 mx-auto mb-6 flex h-20 w-20 items-center justify-center rounded-full bg-gradient-to-br shadow-sm">
|
||||||
<span className="text-3xl">⚠️</span>
|
<span className="text-3xl">⚠️</span>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="mb-3 text-xl font-bold text-foreground">화면을 찾을 수 없습니다</h2>
|
<h2 className="text-foreground mb-3 text-xl font-bold">화면을 찾을 수 없습니다</h2>
|
||||||
<p className="mb-6 leading-relaxed text-muted-foreground">{error || "요청하신 화면이 존재하지 않습니다."}</p>
|
<p className="text-muted-foreground mb-6 leading-relaxed">{error || "요청하신 화면이 존재하지 않습니다."}</p>
|
||||||
<Button onClick={() => router.back()} variant="outline" className="rounded-lg">
|
<Button onClick={() => router.back()} variant="outline" className="rounded-lg">
|
||||||
이전으로 돌아가기
|
이전으로 돌아가기
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -225,7 +225,7 @@ export default function ScreenViewPage() {
|
||||||
{/* 절대 위치 기반 렌더링 */}
|
{/* 절대 위치 기반 렌더링 */}
|
||||||
{layout && layout.components.length > 0 ? (
|
{layout && layout.components.length > 0 ? (
|
||||||
<div
|
<div
|
||||||
className="bg-background relative origin-top-left h-full flex justify-start items-start"
|
className="bg-background relative flex h-full origin-top-left items-start justify-start"
|
||||||
style={{
|
style={{
|
||||||
transform: `scale(${scale})`,
|
transform: `scale(${scale})`,
|
||||||
transformOrigin: "top left",
|
transformOrigin: "top left",
|
||||||
|
|
@ -238,27 +238,76 @@ export default function ScreenViewPage() {
|
||||||
// 🆕 플로우 버튼 그룹 감지 및 처리
|
// 🆕 플로우 버튼 그룹 감지 및 처리
|
||||||
const topLevelComponents = layout.components.filter((component) => !component.parentId);
|
const topLevelComponents = layout.components.filter((component) => !component.parentId);
|
||||||
|
|
||||||
|
// 버튼은 scale에 맞춰 위치만 조정하면 됨 (scale = 1.0이면 그대로, scale < 1.0이면 왼쪽으로)
|
||||||
|
// 하지만 x=0 컴포넌트는 width: 100%로 확장되므로, 그만큼 버튼을 오른쪽으로 이동
|
||||||
|
const leftmostComponent = topLevelComponents.find((c) => c.position.x === 0);
|
||||||
|
let widthOffset = 0;
|
||||||
|
|
||||||
|
if (leftmostComponent && containerWidth > 0) {
|
||||||
|
const originalWidth = leftmostComponent.size?.width || screenWidth;
|
||||||
|
const actualWidth = containerWidth / scale;
|
||||||
|
widthOffset = Math.max(0, actualWidth - originalWidth);
|
||||||
|
|
||||||
|
console.log("📊 widthOffset 계산:", {
|
||||||
|
containerWidth,
|
||||||
|
scale,
|
||||||
|
screenWidth,
|
||||||
|
originalWidth,
|
||||||
|
actualWidth,
|
||||||
|
widthOffset,
|
||||||
|
leftmostType: leftmostComponent.type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const buttonGroups: Record<string, any[]> = {};
|
const buttonGroups: Record<string, any[]> = {};
|
||||||
const processedButtonIds = new Set<string>();
|
const processedButtonIds = new Set<string>();
|
||||||
|
// 🔍 전체 버튼 목록 확인
|
||||||
|
const allButtons = topLevelComponents.filter((component) => {
|
||||||
|
const isButton =
|
||||||
|
(component.type === "component" &&
|
||||||
|
["button-primary", "button-secondary"].includes((component as any).componentType)) ||
|
||||||
|
(component.type === "widget" && (component as any).widgetType === "button");
|
||||||
|
return isButton;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"🔍 메뉴에서 발견된 전체 버튼:",
|
||||||
|
allButtons.map((b) => ({
|
||||||
|
id: b.id,
|
||||||
|
label: b.label,
|
||||||
|
positionX: b.position.x,
|
||||||
|
positionY: b.position.y,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
topLevelComponents.forEach((component) => {
|
topLevelComponents.forEach((component) => {
|
||||||
const isButton =
|
const isButton =
|
||||||
component.type === "button" ||
|
|
||||||
(component.type === "component" &&
|
(component.type === "component" &&
|
||||||
["button-primary", "button-secondary"].includes((component as any).componentType));
|
["button-primary", "button-secondary"].includes((component as any).componentType)) ||
|
||||||
|
(component.type === "widget" && (component as any).widgetType === "button");
|
||||||
|
|
||||||
if (isButton) {
|
if (isButton) {
|
||||||
const flowConfig = (component as any).webTypeConfig?.flowVisibilityConfig as
|
const flowConfig = (component as any).webTypeConfig?.flowVisibilityConfig as
|
||||||
| FlowVisibilityConfig
|
| FlowVisibilityConfig
|
||||||
| undefined;
|
| undefined;
|
||||||
|
|
||||||
if (flowConfig?.enabled && flowConfig.layoutBehavior === "auto-compact" && flowConfig.groupId) {
|
// 🔧 임시: 버튼 그룹 기능 완전 비활성화
|
||||||
|
// TODO: 사용자가 명시적으로 그룹을 원하는 경우에만 활성화하도록 UI 개선 필요
|
||||||
|
const DISABLE_BUTTON_GROUPS = true;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!DISABLE_BUTTON_GROUPS &&
|
||||||
|
flowConfig?.enabled &&
|
||||||
|
flowConfig.layoutBehavior === "auto-compact" &&
|
||||||
|
flowConfig.groupId
|
||||||
|
) {
|
||||||
if (!buttonGroups[flowConfig.groupId]) {
|
if (!buttonGroups[flowConfig.groupId]) {
|
||||||
buttonGroups[flowConfig.groupId] = [];
|
buttonGroups[flowConfig.groupId] = [];
|
||||||
}
|
}
|
||||||
buttonGroups[flowConfig.groupId].push(component);
|
buttonGroups[flowConfig.groupId].push(component);
|
||||||
processedButtonIds.add(component.id);
|
processedButtonIds.add(component.id);
|
||||||
}
|
}
|
||||||
|
// else: 모든 버튼을 개별 렌더링
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -267,92 +316,121 @@ export default function ScreenViewPage() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* 일반 컴포넌트들 */}
|
{/* 일반 컴포넌트들 */}
|
||||||
{regularComponents.map((component) => (
|
{regularComponents.map((component) => {
|
||||||
<RealtimePreview
|
// 버튼인 경우 위치 조정 (테이블이 늘어난 만큼 오른쪽으로 이동)
|
||||||
key={component.id}
|
const isButton =
|
||||||
component={component}
|
(component.type === "component" &&
|
||||||
isSelected={false}
|
["button-primary", "button-secondary"].includes((component as any).componentType)) ||
|
||||||
isDesignMode={false}
|
(component.type === "widget" && (component as any).widgetType === "button");
|
||||||
onClick={() => {}}
|
|
||||||
screenId={screenId}
|
|
||||||
tableName={screen?.tableName}
|
|
||||||
userId={user?.userId}
|
|
||||||
userName={userName}
|
|
||||||
companyCode={companyCode}
|
|
||||||
selectedRowsData={selectedRowsData}
|
|
||||||
onSelectedRowsChange={(_, selectedData) => {
|
|
||||||
console.log("🔍 화면에서 선택된 행 데이터:", selectedData);
|
|
||||||
setSelectedRowsData(selectedData);
|
|
||||||
}}
|
|
||||||
flowSelectedData={flowSelectedData}
|
|
||||||
flowSelectedStepId={flowSelectedStepId}
|
|
||||||
onFlowSelectedDataChange={(selectedData: any[], stepId: number | null) => {
|
|
||||||
setFlowSelectedData(selectedData);
|
|
||||||
setFlowSelectedStepId(stepId);
|
|
||||||
}}
|
|
||||||
refreshKey={tableRefreshKey}
|
|
||||||
onRefresh={() => {
|
|
||||||
setTableRefreshKey((prev) => prev + 1);
|
|
||||||
setSelectedRowsData([]); // 선택 해제
|
|
||||||
}}
|
|
||||||
flowRefreshKey={flowRefreshKey}
|
|
||||||
onFlowRefresh={() => {
|
|
||||||
setFlowRefreshKey((prev) => prev + 1);
|
|
||||||
setFlowSelectedData([]); // 선택 해제
|
|
||||||
setFlowSelectedStepId(null);
|
|
||||||
}}
|
|
||||||
formData={formData}
|
|
||||||
onFormDataChange={(fieldName, value) => {
|
|
||||||
setFormData((prev) => ({ ...prev, [fieldName]: value }));
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* 자식 컴포넌트들 */}
|
|
||||||
{(component.type === "group" || component.type === "container" || component.type === "area") &&
|
|
||||||
layout.components
|
|
||||||
.filter((child) => child.parentId === component.id)
|
|
||||||
.map((child) => {
|
|
||||||
// 자식 컴포넌트의 위치를 부모 기준 상대 좌표로 조정
|
|
||||||
const relativeChildComponent = {
|
|
||||||
...child,
|
|
||||||
position: {
|
|
||||||
x: child.position.x - component.position.x,
|
|
||||||
y: child.position.y - component.position.y,
|
|
||||||
z: child.position.z || 1,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
const adjustedComponent =
|
||||||
<RealtimePreview
|
isButton && widthOffset > 0
|
||||||
key={child.id}
|
? {
|
||||||
component={relativeChildComponent}
|
...component,
|
||||||
isSelected={false}
|
position: {
|
||||||
isDesignMode={false}
|
...component.position,
|
||||||
onClick={() => {}}
|
x: component.position.x + widthOffset,
|
||||||
screenId={screenId}
|
},
|
||||||
tableName={screen?.tableName}
|
}
|
||||||
userId={user?.userId}
|
: component;
|
||||||
userName={userName}
|
|
||||||
companyCode={companyCode}
|
// 버튼일 경우 로그 출력
|
||||||
selectedRowsData={selectedRowsData}
|
if (isButton) {
|
||||||
onSelectedRowsChange={(_, selectedData) => {
|
console.log("🔘 버튼 위치 조정:", {
|
||||||
console.log("🔍 화면에서 선택된 행 데이터 (자식):", selectedData);
|
label: component.label,
|
||||||
setSelectedRowsData(selectedData);
|
originalX: component.position.x,
|
||||||
}}
|
adjustedX: component.position.x + widthOffset,
|
||||||
refreshKey={tableRefreshKey}
|
widthOffset,
|
||||||
onRefresh={() => {
|
});
|
||||||
console.log("🔄 테이블 새로고침 요청됨 (자식)");
|
}
|
||||||
setTableRefreshKey((prev) => prev + 1);
|
|
||||||
setSelectedRowsData([]); // 선택 해제
|
return (
|
||||||
}}
|
<RealtimePreview
|
||||||
formData={formData}
|
key={component.id}
|
||||||
onFormDataChange={(fieldName, value) => {
|
component={adjustedComponent}
|
||||||
setFormData((prev) => ({ ...prev, [fieldName]: value }));
|
isSelected={false}
|
||||||
}}
|
isDesignMode={false}
|
||||||
/>
|
onClick={() => {}}
|
||||||
);
|
screenId={screenId}
|
||||||
})}
|
tableName={screen?.tableName}
|
||||||
</RealtimePreview>
|
userId={user?.userId}
|
||||||
))}
|
userName={userName}
|
||||||
|
companyCode={companyCode}
|
||||||
|
selectedRowsData={selectedRowsData}
|
||||||
|
onSelectedRowsChange={(_, selectedData) => {
|
||||||
|
console.log("🔍 화면에서 선택된 행 데이터:", selectedData);
|
||||||
|
setSelectedRowsData(selectedData);
|
||||||
|
}}
|
||||||
|
flowSelectedData={flowSelectedData}
|
||||||
|
flowSelectedStepId={flowSelectedStepId}
|
||||||
|
onFlowSelectedDataChange={(selectedData: any[], stepId: number | null) => {
|
||||||
|
setFlowSelectedData(selectedData);
|
||||||
|
setFlowSelectedStepId(stepId);
|
||||||
|
}}
|
||||||
|
refreshKey={tableRefreshKey}
|
||||||
|
onRefresh={() => {
|
||||||
|
setTableRefreshKey((prev) => prev + 1);
|
||||||
|
setSelectedRowsData([]); // 선택 해제
|
||||||
|
}}
|
||||||
|
flowRefreshKey={flowRefreshKey}
|
||||||
|
onFlowRefresh={() => {
|
||||||
|
setFlowRefreshKey((prev) => prev + 1);
|
||||||
|
setFlowSelectedData([]); // 선택 해제
|
||||||
|
setFlowSelectedStepId(null);
|
||||||
|
}}
|
||||||
|
formData={formData}
|
||||||
|
onFormDataChange={(fieldName, value) => {
|
||||||
|
setFormData((prev) => ({ ...prev, [fieldName]: value }));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* 자식 컴포넌트들 */}
|
||||||
|
{(component.type === "group" || component.type === "container" || component.type === "area") &&
|
||||||
|
layout.components
|
||||||
|
.filter((child) => child.parentId === component.id)
|
||||||
|
.map((child) => {
|
||||||
|
// 자식 컴포넌트의 위치를 부모 기준 상대 좌표로 조정
|
||||||
|
const relativeChildComponent = {
|
||||||
|
...child,
|
||||||
|
position: {
|
||||||
|
x: child.position.x - component.position.x,
|
||||||
|
y: child.position.y - component.position.y,
|
||||||
|
z: child.position.z || 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RealtimePreview
|
||||||
|
key={child.id}
|
||||||
|
component={relativeChildComponent}
|
||||||
|
isSelected={false}
|
||||||
|
isDesignMode={false}
|
||||||
|
onClick={() => {}}
|
||||||
|
screenId={screenId}
|
||||||
|
tableName={screen?.tableName}
|
||||||
|
userId={user?.userId}
|
||||||
|
userName={userName}
|
||||||
|
companyCode={companyCode}
|
||||||
|
selectedRowsData={selectedRowsData}
|
||||||
|
onSelectedRowsChange={(_, selectedData) => {
|
||||||
|
console.log("🔍 화면에서 선택된 행 데이터 (자식):", selectedData);
|
||||||
|
setSelectedRowsData(selectedData);
|
||||||
|
}}
|
||||||
|
refreshKey={tableRefreshKey}
|
||||||
|
onRefresh={() => {
|
||||||
|
console.log("🔄 테이블 새로고침 요청됨 (자식)");
|
||||||
|
setTableRefreshKey((prev) => prev + 1);
|
||||||
|
setSelectedRowsData([]); // 선택 해제
|
||||||
|
}}
|
||||||
|
formData={formData}
|
||||||
|
onFormDataChange={(fieldName, value) => {
|
||||||
|
setFormData((prev) => ({ ...prev, [fieldName]: value }));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</RealtimePreview>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
{/* 🆕 플로우 버튼 그룹들 */}
|
{/* 🆕 플로우 버튼 그룹들 */}
|
||||||
{Object.entries(buttonGroups).map(([groupId, buttons]) => {
|
{Object.entries(buttonGroups).map(([groupId, buttons]) => {
|
||||||
|
|
@ -372,6 +450,12 @@ export default function ScreenViewPage() {
|
||||||
{ x: buttons[0].position.x, y: buttons[0].position.y, z: buttons[0].position.z || 2 },
|
{ x: buttons[0].position.x, y: buttons[0].position.y, z: buttons[0].position.z || 2 },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 버튼 그룹 위치에도 widthOffset 적용 (테이블이 늘어난 만큼 오른쪽으로 이동)
|
||||||
|
const adjustedGroupPosition = {
|
||||||
|
...groupPosition,
|
||||||
|
x: groupPosition.x + widthOffset,
|
||||||
|
};
|
||||||
|
|
||||||
// 그룹의 크기 계산: 버튼들의 실제 크기 + 간격을 기준으로 계산
|
// 그룹의 크기 계산: 버튼들의 실제 크기 + 간격을 기준으로 계산
|
||||||
const direction = groupConfig.groupDirection || "horizontal";
|
const direction = groupConfig.groupDirection || "horizontal";
|
||||||
const gap = groupConfig.groupGap ?? 8;
|
const gap = groupConfig.groupGap ?? 8;
|
||||||
|
|
|
||||||
|
|
@ -1633,24 +1633,19 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return applyStyles(
|
||||||
<Button
|
<Button
|
||||||
onClick={handleButtonClick}
|
onClick={handleButtonClick}
|
||||||
disabled={readonly}
|
disabled={readonly}
|
||||||
size="sm"
|
size="sm"
|
||||||
variant={config?.variant || "default"}
|
variant={config?.variant || "default"}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
style={{ height: "100%" }}
|
|
||||||
style={{
|
style={{
|
||||||
// 컴포넌트 스타일과 설정 스타일 모두 적용
|
|
||||||
...comp.style,
|
|
||||||
// 크기는 className으로 처리하므로 CSS 크기 속성 제거
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
height: "100%",
|
||||||
// 설정값이 있으면 우선 적용, 없으면 컴포넌트 스타일 사용
|
// 설정값이 있으면 우선 적용
|
||||||
backgroundColor: config?.backgroundColor || comp.style?.backgroundColor,
|
backgroundColor: config?.backgroundColor,
|
||||||
color: config?.textColor || comp.style?.color,
|
color: config?.textColor,
|
||||||
borderColor: config?.borderColor || comp.style?.borderColor,
|
borderColor: config?.borderColor,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{label || "버튼"}
|
{label || "버튼"}
|
||||||
|
|
|
||||||
|
|
@ -425,9 +425,11 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
|
||||||
disabled={config?.disabled}
|
disabled={config?.disabled}
|
||||||
className="h-full w-full"
|
className="h-full w-full"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: config?.backgroundColor,
|
// 컴포넌트 스타일 먼저 적용
|
||||||
color: config?.textColor,
|
|
||||||
...comp.style,
|
...comp.style,
|
||||||
|
// 설정값이 있으면 우선 적용
|
||||||
|
backgroundColor: config?.backgroundColor || comp.style?.backgroundColor,
|
||||||
|
color: config?.textColor || comp.style?.color,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{label || "버튼"}
|
{label || "버튼"}
|
||||||
|
|
|
||||||
|
|
@ -235,17 +235,32 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
|
||||||
return `${size?.height || 40}px`;
|
return `${size?.height || 40}px`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 버튼 컴포넌트인지 확인
|
||||||
|
const isButtonComponent =
|
||||||
|
(component.type === "widget" && (component as WidgetComponent).widgetType === "button") ||
|
||||||
|
(component.type === "component" && (component as any).componentType?.includes("button"));
|
||||||
|
|
||||||
|
// 버튼일 경우 로그 출력 (편집기)
|
||||||
|
if (isButtonComponent && isDesignMode) {
|
||||||
|
console.log("🎨 [편집기] 버튼 위치:", {
|
||||||
|
label: component.label,
|
||||||
|
positionX: position.x,
|
||||||
|
positionY: position.y,
|
||||||
|
sizeWidth: size?.width,
|
||||||
|
sizeHeight: size?.height,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const baseStyle = {
|
const baseStyle = {
|
||||||
left: `${position.x}px`,
|
left: `${position.x}px`,
|
||||||
top: `${position.y}px`,
|
top: `${position.y}px`,
|
||||||
// 🆕 left가 0이면 부모 너비를 100% 채우도록 수정 (우측 여백 제거)
|
// x=0인 컴포넌트는 전체 너비 사용 (버튼 제외)
|
||||||
width: position.x === 0 ? "100%" : getWidth(),
|
width: (position.x === 0 && !isButtonComponent) ? "100%" : getWidth(),
|
||||||
height: getHeight(), // 모든 컴포넌트 고정 높이로 변경
|
height: getHeight(),
|
||||||
zIndex: component.type === "layout" ? 1 : position.z || 2, // 레이아웃은 z-index 1, 다른 컴포넌트는 2 이상
|
zIndex: component.type === "layout" ? 1 : position.z || 2,
|
||||||
...componentStyle,
|
...componentStyle,
|
||||||
// style.width가 있어도 position.x === 0이면 100%로 강제
|
// x=0인 컴포넌트는 100% 너비 강제 (버튼 제외)
|
||||||
...(position.x === 0 && { width: "100%" }),
|
...(position.x === 0 && !isButtonComponent && { width: "100%" }),
|
||||||
// right 속성 강제 제거
|
|
||||||
right: undefined,
|
right: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1250,14 +1250,33 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
|
||||||
|
|
||||||
{/* 실제 컴포넌트 */}
|
{/* 실제 컴포넌트 */}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={(() => {
|
||||||
position: "absolute",
|
const style = {
|
||||||
left: `${component.position.x}px`,
|
position: "absolute" as const,
|
||||||
top: `${component.position.y}px`,
|
left: `${component.position.x}px`,
|
||||||
width: component.style?.width || `${component.size.width}px`,
|
top: `${component.position.y}px`,
|
||||||
height: component.style?.height || `${component.size.height}px`,
|
width: component.style?.width || `${component.size.width}px`,
|
||||||
zIndex: component.position.z || 1,
|
height: component.style?.height || `${component.size.height}px`,
|
||||||
}}
|
zIndex: component.position.z || 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 버튼 타입일 때 디버깅 (widget 타입 또는 component 타입 모두 체크)
|
||||||
|
if (
|
||||||
|
(component.type === "widget" && (component as any).widgetType === "button") ||
|
||||||
|
(component.type === "component" && (component as any).componentType?.includes("button"))
|
||||||
|
) {
|
||||||
|
console.log("🔘 ScreenList 버튼 외부 div 스타일:", {
|
||||||
|
id: component.id,
|
||||||
|
label: component.label,
|
||||||
|
position: component.position,
|
||||||
|
size: component.size,
|
||||||
|
componentStyle: component.style,
|
||||||
|
appliedStyle: style,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return style;
|
||||||
|
})()}
|
||||||
>
|
>
|
||||||
{/* 위젯 컴포넌트가 아닌 경우 DynamicComponentRenderer 사용 */}
|
{/* 위젯 컴포넌트가 아닌 경우 DynamicComponentRenderer 사용 */}
|
||||||
{component.type !== "widget" ? (
|
{component.type !== "widget" ? (
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,11 @@ export const ButtonWidget: React.FC<WebTypeComponentProps> = ({
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
disabled={disabled || readonly}
|
disabled={disabled || readonly}
|
||||||
className={`rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors duration-200 hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:bg-gray-300 disabled:text-gray-500 ${className || ""} `}
|
className={`rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors duration-200 hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:bg-gray-300 disabled:text-gray-500 ${className || ""} `}
|
||||||
style={style}
|
style={{
|
||||||
|
...style,
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
title={config?.tooltip || placeholder}
|
title={config?.tooltip || placeholder}
|
||||||
>
|
>
|
||||||
{config?.label || config?.text || value || placeholder || "버튼"}
|
{config?.label || config?.text || value || placeholder || "버튼"}
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,16 @@ export const DynamicWebTypeRenderer: React.FC<DynamicComponentProps> = ({
|
||||||
const { webTypes } = useWebTypes({ active: "Y" });
|
const { webTypes } = useWebTypes({ active: "Y" });
|
||||||
|
|
||||||
// 디버깅: 전달받은 웹타입과 props 정보 로깅
|
// 디버깅: 전달받은 웹타입과 props 정보 로깅
|
||||||
console.log("🔍 DynamicWebTypeRenderer 호출:", {
|
if (webType === "button") {
|
||||||
webType,
|
console.log("🔘 DynamicWebTypeRenderer 버튼 호출:", {
|
||||||
propsKeys: Object.keys(props),
|
webType,
|
||||||
component: props.component,
|
component: props.component,
|
||||||
isFileComponent: props.component?.type === "file" || webType === "file",
|
position: props.component?.position,
|
||||||
});
|
size: props.component?.size,
|
||||||
|
style: props.component?.style,
|
||||||
|
config,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const webTypeDefinition = useMemo(() => {
|
const webTypeDefinition = useMemo(() => {
|
||||||
return WebTypeRegistry.getWebType(webType);
|
return WebTypeRegistry.getWebType(webType);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue