버튼 수정
This commit is contained in:
parent
7aecae559b
commit
d64ca5a8c0
|
|
@ -189,10 +189,10 @@ export default function ScreenViewPage() {
|
|||
|
||||
if (loading) {
|
||||
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="rounded-xl border border-border bg-background p-8 text-center shadow-lg">
|
||||
<Loader2 className="mx-auto h-10 w-10 animate-spin text-primary" />
|
||||
<p className="mt-4 font-medium text-foreground">화면을 불러오는 중...</p>
|
||||
<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="border-border bg-background rounded-xl border p-8 text-center shadow-lg">
|
||||
<Loader2 className="text-primary mx-auto h-10 w-10 animate-spin" />
|
||||
<p className="text-foreground mt-4 font-medium">화면을 불러오는 중...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -200,13 +200,13 @@ export default function ScreenViewPage() {
|
|||
|
||||
if (error || !screen) {
|
||||
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="max-w-md rounded-xl border border-border bg-background 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-muted to-muted/50 flex h-full min-h-[400px] w-full items-center justify-center bg-gradient-to-br">
|
||||
<div className="border-border bg-background max-w-md rounded-xl border p-8 text-center shadow-lg">
|
||||
<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>
|
||||
</div>
|
||||
<h2 className="mb-3 text-xl font-bold text-foreground">화면을 찾을 수 없습니다</h2>
|
||||
<p className="mb-6 leading-relaxed text-muted-foreground">{error || "요청하신 화면이 존재하지 않습니다."}</p>
|
||||
<h2 className="text-foreground mb-3 text-xl font-bold">화면을 찾을 수 없습니다</h2>
|
||||
<p className="text-muted-foreground mb-6 leading-relaxed">{error || "요청하신 화면이 존재하지 않습니다."}</p>
|
||||
<Button onClick={() => router.back()} variant="outline" className="rounded-lg">
|
||||
이전으로 돌아가기
|
||||
</Button>
|
||||
|
|
@ -225,7 +225,7 @@ export default function ScreenViewPage() {
|
|||
{/* 절대 위치 기반 렌더링 */}
|
||||
{layout && layout.components.length > 0 ? (
|
||||
<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={{
|
||||
transform: `scale(${scale})`,
|
||||
transformOrigin: "top left",
|
||||
|
|
@ -238,27 +238,76 @@ export default function ScreenViewPage() {
|
|||
// 🆕 플로우 버튼 그룹 감지 및 처리
|
||||
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 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) => {
|
||||
const isButton =
|
||||
component.type === "button" ||
|
||||
(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) {
|
||||
const flowConfig = (component as any).webTypeConfig?.flowVisibilityConfig as
|
||||
| FlowVisibilityConfig
|
||||
| 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]) {
|
||||
buttonGroups[flowConfig.groupId] = [];
|
||||
}
|
||||
buttonGroups[flowConfig.groupId].push(component);
|
||||
processedButtonIds.add(component.id);
|
||||
}
|
||||
// else: 모든 버튼을 개별 렌더링
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -267,10 +316,38 @@ export default function ScreenViewPage() {
|
|||
return (
|
||||
<>
|
||||
{/* 일반 컴포넌트들 */}
|
||||
{regularComponents.map((component) => (
|
||||
{regularComponents.map((component) => {
|
||||
// 버튼인 경우 위치 조정 (테이블이 늘어난 만큼 오른쪽으로 이동)
|
||||
const isButton =
|
||||
(component.type === "component" &&
|
||||
["button-primary", "button-secondary"].includes((component as any).componentType)) ||
|
||||
(component.type === "widget" && (component as any).widgetType === "button");
|
||||
|
||||
const adjustedComponent =
|
||||
isButton && widthOffset > 0
|
||||
? {
|
||||
...component,
|
||||
position: {
|
||||
...component.position,
|
||||
x: component.position.x + widthOffset,
|
||||
},
|
||||
}
|
||||
: component;
|
||||
|
||||
// 버튼일 경우 로그 출력
|
||||
if (isButton) {
|
||||
console.log("🔘 버튼 위치 조정:", {
|
||||
label: component.label,
|
||||
originalX: component.position.x,
|
||||
adjustedX: component.position.x + widthOffset,
|
||||
widthOffset,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<RealtimePreview
|
||||
key={component.id}
|
||||
component={component}
|
||||
component={adjustedComponent}
|
||||
isSelected={false}
|
||||
isDesignMode={false}
|
||||
onClick={() => {}}
|
||||
|
|
@ -352,7 +429,8 @@ export default function ScreenViewPage() {
|
|||
);
|
||||
})}
|
||||
</RealtimePreview>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
|
||||
{/* 🆕 플로우 버튼 그룹들 */}
|
||||
{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 },
|
||||
);
|
||||
|
||||
// 버튼 그룹 위치에도 widthOffset 적용 (테이블이 늘어난 만큼 오른쪽으로 이동)
|
||||
const adjustedGroupPosition = {
|
||||
...groupPosition,
|
||||
x: groupPosition.x + widthOffset,
|
||||
};
|
||||
|
||||
// 그룹의 크기 계산: 버튼들의 실제 크기 + 간격을 기준으로 계산
|
||||
const direction = groupConfig.groupDirection || "horizontal";
|
||||
const gap = groupConfig.groupGap ?? 8;
|
||||
|
|
|
|||
|
|
@ -1633,24 +1633,19 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
|||
}
|
||||
};
|
||||
|
||||
return (
|
||||
return applyStyles(
|
||||
<Button
|
||||
onClick={handleButtonClick}
|
||||
disabled={readonly}
|
||||
size="sm"
|
||||
variant={config?.variant || "default"}
|
||||
className="w-full"
|
||||
style={{ height: "100%" }}
|
||||
style={{
|
||||
// 컴포넌트 스타일과 설정 스타일 모두 적용
|
||||
...comp.style,
|
||||
// 크기는 className으로 처리하므로 CSS 크기 속성 제거
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
// 설정값이 있으면 우선 적용, 없으면 컴포넌트 스타일 사용
|
||||
backgroundColor: config?.backgroundColor || comp.style?.backgroundColor,
|
||||
color: config?.textColor || comp.style?.color,
|
||||
borderColor: config?.borderColor || comp.style?.borderColor,
|
||||
// 설정값이 있으면 우선 적용
|
||||
backgroundColor: config?.backgroundColor,
|
||||
color: config?.textColor,
|
||||
borderColor: config?.borderColor,
|
||||
}}
|
||||
>
|
||||
{label || "버튼"}
|
||||
|
|
|
|||
|
|
@ -425,9 +425,11 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
|
|||
disabled={config?.disabled}
|
||||
className="h-full w-full"
|
||||
style={{
|
||||
backgroundColor: config?.backgroundColor,
|
||||
color: config?.textColor,
|
||||
// 컴포넌트 스타일 먼저 적용
|
||||
...comp.style,
|
||||
// 설정값이 있으면 우선 적용
|
||||
backgroundColor: config?.backgroundColor || comp.style?.backgroundColor,
|
||||
color: config?.textColor || comp.style?.color,
|
||||
}}
|
||||
>
|
||||
{label || "버튼"}
|
||||
|
|
|
|||
|
|
@ -235,17 +235,32 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
|
|||
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 = {
|
||||
left: `${position.x}px`,
|
||||
top: `${position.y}px`,
|
||||
// 🆕 left가 0이면 부모 너비를 100% 채우도록 수정 (우측 여백 제거)
|
||||
width: position.x === 0 ? "100%" : getWidth(),
|
||||
height: getHeight(), // 모든 컴포넌트 고정 높이로 변경
|
||||
zIndex: component.type === "layout" ? 1 : position.z || 2, // 레이아웃은 z-index 1, 다른 컴포넌트는 2 이상
|
||||
// x=0인 컴포넌트는 전체 너비 사용 (버튼 제외)
|
||||
width: (position.x === 0 && !isButtonComponent) ? "100%" : getWidth(),
|
||||
height: getHeight(),
|
||||
zIndex: component.type === "layout" ? 1 : position.z || 2,
|
||||
...componentStyle,
|
||||
// style.width가 있어도 position.x === 0이면 100%로 강제
|
||||
...(position.x === 0 && { width: "100%" }),
|
||||
// right 속성 강제 제거
|
||||
// x=0인 컴포넌트는 100% 너비 강제 (버튼 제외)
|
||||
...(position.x === 0 && !isButtonComponent && { width: "100%" }),
|
||||
right: undefined,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1250,14 +1250,33 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
|
|||
|
||||
{/* 실제 컴포넌트 */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
style={(() => {
|
||||
const style = {
|
||||
position: "absolute" as const,
|
||||
left: `${component.position.x}px`,
|
||||
top: `${component.position.y}px`,
|
||||
width: component.style?.width || `${component.size.width}px`,
|
||||
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 사용 */}
|
||||
{component.type !== "widget" ? (
|
||||
|
|
|
|||
|
|
@ -31,7 +31,11 @@ export const ButtonWidget: React.FC<WebTypeComponentProps> = ({
|
|||
onClick={handleClick}
|
||||
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 || ""} `}
|
||||
style={style}
|
||||
style={{
|
||||
...style,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
title={config?.tooltip || placeholder}
|
||||
>
|
||||
{config?.label || config?.text || value || placeholder || "버튼"}
|
||||
|
|
|
|||
|
|
@ -21,12 +21,16 @@ export const DynamicWebTypeRenderer: React.FC<DynamicComponentProps> = ({
|
|||
const { webTypes } = useWebTypes({ active: "Y" });
|
||||
|
||||
// 디버깅: 전달받은 웹타입과 props 정보 로깅
|
||||
console.log("🔍 DynamicWebTypeRenderer 호출:", {
|
||||
if (webType === "button") {
|
||||
console.log("🔘 DynamicWebTypeRenderer 버튼 호출:", {
|
||||
webType,
|
||||
propsKeys: Object.keys(props),
|
||||
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(() => {
|
||||
return WebTypeRegistry.getWebType(webType);
|
||||
|
|
|
|||
Loading…
Reference in New Issue