fix: 화면 편집기에서 버튼 컴포넌트 선택 가능하도록 수정

- 문제: 버튼 컴포넌트 클릭 시 버튼 동작이 실행되어 선택되지 않음
- 해결:
  1. ButtonPrimaryComponent에서 디자인 모드일 때 <button> 대신 <div>로 렌더링
  2. ScreenDesigner의 ScreenPreviewProvider에서 isPreviewMode를 false로 설정
  3. 디자인 모드에서는 버튼 액션이 실행되지 않고 onClick만 전달되도록 수정
- 영향: button-primary 타입 버튼이 화면 편집기에서 정상적으로 선택 가능
This commit is contained in:
kjs 2025-11-10 15:36:18 +09:00
parent 2d832c56b6
commit cdf9c0e562
4 changed files with 85 additions and 43 deletions

View File

@ -563,7 +563,7 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
{/* 위젯 타입 - 동적 렌더링 (파일 컴포넌트 제외) */}
{type === "widget" && !isFileComponent(component) && (
<div className="pointer-events-none h-full w-full">
<div className="h-full w-full">
<WidgetRenderer
component={component}
isDesignMode={isDesignMode}

View File

@ -4124,7 +4124,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
}
return (
<ScreenPreviewProvider isPreviewMode={true}>
<ScreenPreviewProvider isPreviewMode={false}>
<div className="bg-background flex h-full w-full flex-col">
{/* 상단 슬림 툴바 */}
<SlimToolbar

View File

@ -14,10 +14,17 @@ export const ButtonWidget: React.FC<WebTypeComponentProps> = ({
required,
className,
style,
isDesignMode = false, // 디자인 모드 플래그
...restProps
}) => {
const handleClick = () => {
const handleClick = (e: React.MouseEvent) => {
// 디자인 모드에서는 아무것도 하지 않고 그냥 이벤트 전파
if (isDesignMode) {
return;
}
// 버튼 클릭 시 동작 (추후 버튼 액션 시스템과 연동)
// console.log("Button clicked:", config);
console.log("Button clicked:", config);
// onChange를 통해 클릭 이벤트 전달
if (onChange) {
@ -25,6 +32,25 @@ export const ButtonWidget: React.FC<WebTypeComponentProps> = ({
}
};
// 디자인 모드에서는 div로 렌더링하여 버튼 동작 완전 차단
if (isDesignMode) {
return (
<div
onClick={handleClick} // 클릭 핸들러 추가하여 이벤트 전파
className={`flex items-center justify-center rounded-md bg-blue-600 px-4 text-sm font-medium text-white ${className || ""} `}
style={{
...style,
width: "100%",
height: "100%",
cursor: "pointer", // 선택 가능하도록 포인터 표시
}}
title={config?.tooltip || placeholder}
>
{config?.label || config?.text || value || placeholder || "버튼"}
</div>
);
}
return (
<button
type="button"

View File

@ -528,48 +528,64 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
}
}
// 공통 버튼 스타일
const buttonElementStyle: React.CSSProperties = {
width: "100%",
height: "100%",
minHeight: "40px",
border: "none",
borderRadius: "0.5rem",
background: componentConfig.disabled ? "#e5e7eb" : buttonColor,
color: componentConfig.disabled ? "#9ca3af" : "white",
// 🔧 크기 설정 적용 (sm/md/lg)
fontSize: componentConfig.size === "sm" ? "0.75rem" : componentConfig.size === "lg" ? "1rem" : "0.875rem",
fontWeight: "600",
cursor: componentConfig.disabled ? "not-allowed" : "pointer",
outline: "none",
boxSizing: "border-box",
display: "flex",
alignItems: "center",
justifyContent: "center",
// 🔧 크기에 따른 패딩 조정
padding:
componentConfig.size === "sm" ? "0 0.75rem" : componentConfig.size === "lg" ? "0 1.25rem" : "0 1rem",
margin: "0",
lineHeight: "1.25",
boxShadow: componentConfig.disabled ? "none" : "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
// isInteractive 모드에서는 사용자 스타일 우선 적용 (width/height 제외)
...(isInteractive && component.style ? Object.fromEntries(
Object.entries(component.style).filter(([key]) => key !== 'width' && key !== 'height')
) : {}),
};
const buttonContent = processedConfig.text !== undefined ? processedConfig.text : component.label || "버튼";
return (
<>
<div style={componentStyle} className={className} {...safeDomProps}>
<button
type={componentConfig.actionType || "button"}
disabled={componentConfig.disabled || false}
className="transition-colors duration-150 hover:opacity-90 active:scale-95 transition-transform"
style={{
width: "100%",
height: "100%",
minHeight: "40px",
border: "none",
borderRadius: "0.5rem",
background: componentConfig.disabled ? "#e5e7eb" : buttonColor,
color: componentConfig.disabled ? "#9ca3af" : "white",
// 🔧 크기 설정 적용 (sm/md/lg)
fontSize: componentConfig.size === "sm" ? "0.75rem" : componentConfig.size === "lg" ? "1rem" : "0.875rem",
fontWeight: "600",
cursor: componentConfig.disabled ? "not-allowed" : "pointer",
outline: "none",
boxSizing: "border-box",
display: "flex",
alignItems: "center",
justifyContent: "center",
// 🔧 크기에 따른 패딩 조정
padding:
componentConfig.size === "sm" ? "0 0.75rem" : componentConfig.size === "lg" ? "0 1.25rem" : "0 1rem",
margin: "0",
lineHeight: "1.25",
boxShadow: componentConfig.disabled ? "none" : "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
// isInteractive 모드에서는 사용자 스타일 우선 적용 (width/height 제외)
...(isInteractive && component.style ? Object.fromEntries(
Object.entries(component.style).filter(([key]) => key !== 'width' && key !== 'height')
) : {}),
}}
onClick={handleClick}
onDragStart={onDragStart}
onDragEnd={onDragEnd}
>
{/* 🔧 빈 문자열도 허용 (undefined일 때만 기본값 적용) */}
{processedConfig.text !== undefined ? processedConfig.text : component.label || "버튼"}
</button>
{isDesignMode ? (
// 디자인 모드: div로 렌더링하여 선택 가능하게 함
<div
className="transition-colors duration-150 hover:opacity-90"
style={buttonElementStyle}
onClick={handleClick}
>
{buttonContent}
</div>
) : (
// 일반 모드: button으로 렌더링
<button
type={componentConfig.actionType || "button"}
disabled={componentConfig.disabled || false}
className="transition-colors duration-150 hover:opacity-90 active:scale-95 transition-transform"
style={buttonElementStyle}
onClick={handleClick}
onDragStart={onDragStart}
onDragEnd={onDragEnd}
>
{buttonContent}
</button>
)}
</div>
{/* 확인 다이얼로그 - EditModal보다 위에 표시하도록 z-index 최상위로 설정 */}