라벨표시 수정

This commit is contained in:
kjs 2025-09-22 14:13:05 +09:00
parent 4b28530fec
commit 0258c2a76c
14 changed files with 235 additions and 144 deletions

View File

@ -467,8 +467,8 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
<> <>
<div className="absolute" style={componentStyle}> <div className="absolute" style={componentStyle}>
<div className="h-full w-full"> <div className="h-full w-full">
{/* 라벨 표시 */} {/* 라벨 표시 - 컴포넌트 내부에서 라벨을 처리하므로 외부에서는 표시하지 않음 */}
{!hideLabel && component.label && ( {!hideLabel && component.label && component.style?.labelDisplay === false && (
<div className="mb-1"> <div className="mb-1">
<label className="text-sm font-medium text-gray-700"> <label className="text-sm font-medium text-gray-700">
{component.label} {component.label}

View File

@ -1884,7 +1884,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
codeCategory: column.codeCategory, codeCategory: column.codeCategory,
}), }),
style: { style: {
labelDisplay: false, // 모든 컴포넌트의 기본 라벨 표시를 false로 설정 labelDisplay: true, // 테이블 패널에서 드래그한 컴포넌트는 라벨을 기본적으로 표시
labelFontSize: "12px", labelFontSize: "12px",
labelColor: "#374151", labelColor: "#374151",
labelFontWeight: "500", labelFontWeight: "500",

View File

@ -77,7 +77,7 @@ export const CheckboxBasicComponent: React.FC<CheckboxBasicComponentProps> = ({
return ( return (
<div style={componentStyle} className={className} {...domProps}> <div style={componentStyle} className={className} {...domProps}>
{/* 라벨 렌더링 */} {/* 라벨 렌더링 */}
{component.label && ( {component.label && component.style?.labelDisplay !== false && (
<label <label
style={{ style={{
position: "absolute", position: "absolute",
@ -92,11 +92,13 @@ export const CheckboxBasicComponent: React.FC<CheckboxBasicComponentProps> = ({
> >
{component.label} {component.label}
{component.required && ( {component.required && (
<span style={{ <span
style={{
color: "#ef4444", color: "#ef4444",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}), ...(isInteractive && component.style ? component.style : {}),
}}> }}
>
* *
</span> </span>
)} )}
@ -104,7 +106,8 @@ export const CheckboxBasicComponent: React.FC<CheckboxBasicComponentProps> = ({
)} )}
<label <label
style={{display: "flex", style={{
display: "flex",
alignItems: "center", alignItems: "center",
gap: "8px", gap: "8px",
cursor: "pointer", cursor: "pointer",
@ -112,7 +115,8 @@ export const CheckboxBasicComponent: React.FC<CheckboxBasicComponentProps> = ({
height: "100%", height: "100%",
fontSize: "14px", fontSize: "14px",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),}} ...(isInteractive && component.style ? component.style : {}),
}}
onClick={handleClick} onClick={handleClick}
onDragStart={onDragStart} onDragStart={onDragStart}
onDragEnd={onDragEnd} onDragEnd={onDragEnd}
@ -122,20 +126,28 @@ export const CheckboxBasicComponent: React.FC<CheckboxBasicComponentProps> = ({
checked={component.value === true || component.value === "true"} checked={component.value === true || component.value === "true"}
disabled={componentConfig.disabled || false} disabled={componentConfig.disabled || false}
required={componentConfig.required || false} required={componentConfig.required || false}
style={{width: "16px", style={{
width: "16px",
height: "16px", height: "16px",
accentColor: "#3b82f6", accentColor: "#3b82f6",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),}} ...(isInteractive && component.style ? component.style : {}),
}}
onChange={(e) => { onChange={(e) => {
if (component.onChange) { if (component.onChange) {
component.onChange(e.target.checked); component.onChange(e.target.checked);
} }
}} }}
/> />
<span style={{color: "#374151", <span
style={{
color: "#374151",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),}}>{componentConfig.checkboxLabel || component.text || "체크박스"}</span> ...(isInteractive && component.style ? component.style : {}),
}}
>
{componentConfig.checkboxLabel || component.text || "체크박스"}
</span>
</label> </label>
</div> </div>
); );

View File

@ -303,15 +303,7 @@ export const DateInputComponent: React.FC<DateInputComponentProps> = ({
}} }}
> >
{component.label} {component.label}
{component.required && ( {component.required && <span style={{ color: "#ef4444" }}>*</span>}
<span
style={{
color: "#ef4444",
}}
>
*
</span>
)}
</label> </label>
)} )}

View File

@ -74,7 +74,7 @@ export const DividerLineComponent: React.FC<DividerLineComponentProps> = ({
return ( return (
<div style={componentStyle} className={className} {...domProps}> <div style={componentStyle} className={className} {...domProps}>
{/* 라벨 렌더링 */} {/* 라벨 렌더링 */}
{component.label && ( {component.label && component.style?.labelDisplay !== false && (
<label <label
style={{ style={{
position: "absolute", position: "absolute",

View File

@ -77,7 +77,7 @@ export const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
return ( return (
<div style={componentStyle} className={className} {...domProps}> <div style={componentStyle} className={className} {...domProps}>
{/* 라벨 렌더링 */} {/* 라벨 렌더링 */}
{component.label && ( {component.label && component.style?.labelDisplay !== false && (
<label <label
style={{ style={{
position: "absolute", position: "absolute",
@ -91,15 +91,23 @@ export const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
}} }}
> >
{component.label} {component.label}
{component.required && <span style={{color: "#ef4444", {component.required && (
<span
style={{
color: "#ef4444",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}), ...(isInteractive && component.style ? component.style : {}),
}}>*</span>} }}
>
*
</span>
)}
</label> </label>
)} )}
<div <div
style={{width: "100%", style={{
width: "100%",
height: "100%", height: "100%",
border: "2px dashed #d1d5db", border: "2px dashed #d1d5db",
borderRadius: "8px", borderRadius: "8px",
@ -139,22 +147,42 @@ export const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
} }
}} }}
/> />
<div style={{textAlign: "center", color: "#6b7280", fontSize: "14px", <div
style={{
textAlign: "center",
color: "#6b7280",
fontSize: "14px",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}), ...(isInteractive && component.style ? component.style : {}),
}}> }}
<div style={{fontSize: "24px", marginBottom: "8px", >
<div
style={{
fontSize: "24px",
marginBottom: "8px",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}), ...(isInteractive && component.style ? component.style : {}),
}}>📁</div> }}
<div style={{fontWeight: "500", >
📁
</div>
<div
style={{
fontWeight: "500",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}), ...(isInteractive && component.style ? component.style : {}),
}}> </div> }}
<div style={{fontSize: "12px", marginTop: "4px", >
</div>
<div
style={{
fontSize: "12px",
marginTop: "4px",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}), ...(isInteractive && component.style ? component.style : {}),
}}> }}
>
{componentConfig.accept && `지원 형식: ${componentConfig.accept}`} {componentConfig.accept && `지원 형식: ${componentConfig.accept}`}
</div> </div>
</div> </div>

View File

@ -74,7 +74,7 @@ export const ImageDisplayComponent: React.FC<ImageDisplayComponentProps> = ({
return ( return (
<div style={componentStyle} className={className} {...domProps}> <div style={componentStyle} className={className} {...domProps}>
{/* 라벨 렌더링 */} {/* 라벨 렌더링 */}
{component.label && ( {component.label && component.style?.labelDisplay !== false && (
<label <label
style={{ style={{
position: "absolute", position: "absolute",

View File

@ -96,15 +96,7 @@ export const NumberInputComponent: React.FC<NumberInputComponentProps> = ({
}} }}
> >
{component.label} {component.label}
{component.required && ( {component.required && <span style={{ color: "#ef4444" }}>*</span>}
<span
style={{
color: "#ef4444",
}}
>
*
</span>
)}
</label> </label>
)} )}

View File

@ -77,7 +77,7 @@ export const RadioBasicComponent: React.FC<RadioBasicComponentProps> = ({
return ( return (
<div style={componentStyle} className={className} {...domProps}> <div style={componentStyle} className={className} {...domProps}>
{/* 라벨 렌더링 */} {/* 라벨 렌더링 */}
{component.label && ( {component.label && component.style?.labelDisplay !== false && (
<label <label
style={{ style={{
position: "absolute", position: "absolute",
@ -91,15 +91,23 @@ export const RadioBasicComponent: React.FC<RadioBasicComponentProps> = ({
}} }}
> >
{component.label} {component.label}
{component.required && <span style={{color: "#ef4444", {component.required && (
<span
style={{
color: "#ef4444",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}), ...(isInteractive && component.style ? component.style : {}),
}}>*</span>} }}
>
*
</span>
)}
</label> </label>
)} )}
<div <div
style={{width: "100%", style={{
width: "100%",
height: "100%", height: "100%",
display: "flex", display: "flex",
flexDirection: componentConfig.direction === "horizontal" ? "row" : "column", flexDirection: componentConfig.direction === "horizontal" ? "row" : "column",
@ -115,7 +123,8 @@ export const RadioBasicComponent: React.FC<RadioBasicComponentProps> = ({
{(componentConfig.options || []).map((option, index) => ( {(componentConfig.options || []).map((option, index) => (
<label <label
key={index} key={index}
style={{display: "flex", style={{
display: "flex",
alignItems: "center", alignItems: "center",
gap: "6px", gap: "6px",
cursor: "pointer", cursor: "pointer",
@ -131,7 +140,8 @@ export const RadioBasicComponent: React.FC<RadioBasicComponentProps> = ({
checked={component.value === option.value} checked={component.value === option.value}
disabled={componentConfig.disabled || false} disabled={componentConfig.disabled || false}
required={componentConfig.required || false} required={componentConfig.required || false}
style={{width: "16px", style={{
width: "16px",
height: "16px", height: "16px",
accentColor: "#3b82f6", accentColor: "#3b82f6",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
@ -143,38 +153,63 @@ export const RadioBasicComponent: React.FC<RadioBasicComponentProps> = ({
} }
}} }}
/> />
<span style={{color: "#374151", <span
style={{
color: "#374151",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}), ...(isInteractive && component.style ? component.style : {}),
}}>{option.label}</span> }}
>
{option.label}
</span>
</label> </label>
))} ))}
{(!componentConfig.options || componentConfig.options.length === 0) && ( {(!componentConfig.options || componentConfig.options.length === 0) && (
<> <>
<label style={{display: "flex", alignItems: "center", gap: "6px", cursor: "pointer", fontSize: "14px", <label
style={{
display: "flex",
alignItems: "center",
gap: "6px",
cursor: "pointer",
fontSize: "14px",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}), ...(isInteractive && component.style ? component.style : {}),
}}> }}
>
<input <input
type="radio" type="radio"
name={component.id || "radio-group"} name={component.id || "radio-group"}
value="option1" value="option1"
style={{width: "16px", height: "16px", accentColor: "#3b82f6", style={{
width: "16px",
height: "16px",
accentColor: "#3b82f6",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}), ...(isInteractive && component.style ? component.style : {}),
}} }}
/> />
<span> 1</span> <span> 1</span>
</label> </label>
<label style={{display: "flex", alignItems: "center", gap: "6px", cursor: "pointer", fontSize: "14px", <label
style={{
display: "flex",
alignItems: "center",
gap: "6px",
cursor: "pointer",
fontSize: "14px",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}), ...(isInteractive && component.style ? component.style : {}),
}}> }}
>
<input <input
type="radio" type="radio"
name={component.id || "radio-group"} name={component.id || "radio-group"}
value="option2" value="option2"
style={{width: "16px", height: "16px", accentColor: "#3b82f6", style={{
width: "16px",
height: "16px",
accentColor: "#3b82f6",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}), ...(isInteractive && component.style ? component.style : {}),
}} }}

View File

@ -58,7 +58,7 @@ const loadGlobalTableCodeCategory = async (tableName: string, columnName: string
try { try {
await globalState.activeRequests.get(`table_${key}`); await globalState.activeRequests.get(`table_${key}`);
} catch (error) { } catch (error) {
console.error(`❌ 테이블 설정 로딩 대기 중 오류:`, error); console.error("❌ 테이블 설정 로딩 대기 중 오류:", error);
} }
} }
@ -111,7 +111,7 @@ const loadGlobalCodeOptions = async (codeCategory: string): Promise<Option[]> =>
try { try {
await globalState.activeRequests.get(`code_${codeCategory}`); await globalState.activeRequests.get(`code_${codeCategory}`);
} catch (error) { } catch (error) {
console.error(`❌ 코드 옵션 로딩 대기 중 오류:`, error); console.error("❌ 코드 옵션 로딩 대기 중 오류:", error);
} }
} }
@ -281,7 +281,7 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
// 값이 실제로 다른 경우에만 업데이트 (빈 문자열도 유효한 값으로 처리) // 값이 실제로 다른 경우에만 업데이트 (빈 문자열도 유효한 값으로 처리)
if (newValue !== selectedValue) { if (newValue !== selectedValue) {
console.log(`🔄 SelectBasicComponent value 업데이트: "${selectedValue}" → "${newValue}"`); console.log(`🔄 SelectBasicComponent value 업데이트: "${selectedValue}" → "${newValue}"`);
console.log(`🔍 업데이트 조건 분석:`, { console.log("🔍 업데이트 조건 분석:", {
externalValue, externalValue,
componentConfigValue: componentConfig?.value, componentConfigValue: componentConfig?.value,
configValue: config?.value, configValue: config?.value,

View File

@ -77,7 +77,7 @@ export const SliderBasicComponent: React.FC<SliderBasicComponentProps> = ({
return ( return (
<div style={componentStyle} className={className} {...domProps}> <div style={componentStyle} className={className} {...domProps}>
{/* 라벨 렌더링 */} {/* 라벨 렌더링 */}
{component.label && ( {component.label && component.style?.labelDisplay !== false && (
<label <label
style={{ style={{
position: "absolute", position: "absolute",
@ -91,15 +91,23 @@ export const SliderBasicComponent: React.FC<SliderBasicComponentProps> = ({
}} }}
> >
{component.label} {component.label}
{component.required && <span style={{color: "#ef4444", {component.required && (
<span
style={{
color: "#ef4444",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}), ...(isInteractive && component.style ? component.style : {}),
}}>*</span>} }}
>
*
</span>
)}
</label> </label>
)} )}
<div <div
style={{width: "100%", style={{
width: "100%",
height: "100%", height: "100%",
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
@ -120,7 +128,8 @@ export const SliderBasicComponent: React.FC<SliderBasicComponentProps> = ({
value={component.value || componentConfig.min || 0} value={component.value || componentConfig.min || 0}
disabled={componentConfig.disabled || false} disabled={componentConfig.disabled || false}
required={componentConfig.required || false} required={componentConfig.required || false}
style={{width: "70%", style={{
width: "70%",
height: "6px", height: "6px",
outline: "none", outline: "none",
borderRadius: "3px", borderRadius: "3px",
@ -136,7 +145,8 @@ export const SliderBasicComponent: React.FC<SliderBasicComponentProps> = ({
}} }}
/> />
<span <span
style={{width: "30%", style={{
width: "30%",
textAlign: "center", textAlign: "center",
fontSize: "14px", fontSize: "14px",
color: "#374151", color: "#374151",

View File

@ -74,7 +74,7 @@ export const TestInputComponent: React.FC<TestInputComponentProps> = ({
return ( return (
<div style={componentStyle} className={className} {...domProps}> <div style={componentStyle} className={className} {...domProps}>
{/* 라벨 렌더링 */} {/* 라벨 렌더링 */}
{component.label && ( {component.label && component.style?.labelDisplay !== false && (
<label <label
style={{ style={{
position: "absolute", position: "absolute",

View File

@ -77,7 +77,7 @@ export const TextareaBasicComponent: React.FC<TextareaBasicComponentProps> = ({
return ( return (
<div style={componentStyle} className={className} {...domProps}> <div style={componentStyle} className={className} {...domProps}>
{/* 라벨 렌더링 */} {/* 라벨 렌더링 */}
{component.label && ( {component.label && component.style?.labelDisplay !== false && (
<label <label
style={{ style={{
position: "absolute", position: "absolute",
@ -91,10 +91,17 @@ export const TextareaBasicComponent: React.FC<TextareaBasicComponentProps> = ({
}} }}
> >
{component.label} {component.label}
{component.required && <span style={{color: "#ef4444", {component.required && (
<span
style={{
color: "#ef4444",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}), ...(isInteractive && component.style ? component.style : {}),
}}>*</span>} }}
>
*
</span>
)}
</label> </label>
)} )}
@ -105,7 +112,8 @@ export const TextareaBasicComponent: React.FC<TextareaBasicComponentProps> = ({
required={componentConfig.required || false} required={componentConfig.required || false}
readOnly={componentConfig.readonly || false} readOnly={componentConfig.readonly || false}
rows={componentConfig.rows || 3} rows={componentConfig.rows || 3}
style={{width: "100%", style={{
width: "100%",
height: "100%", height: "100%",
border: "1px solid #d1d5db", border: "1px solid #d1d5db",
borderRadius: "4px", borderRadius: "4px",

View File

@ -77,7 +77,7 @@ export const ToggleSwitchComponent: React.FC<ToggleSwitchComponentProps> = ({
return ( return (
<div style={componentStyle} className={className} {...domProps}> <div style={componentStyle} className={className} {...domProps}>
{/* 라벨 렌더링 */} {/* 라벨 렌더링 */}
{component.label && ( {component.label && component.style?.labelDisplay !== false && (
<label <label
style={{ style={{
position: "absolute", position: "absolute",
@ -91,15 +91,23 @@ export const ToggleSwitchComponent: React.FC<ToggleSwitchComponentProps> = ({
}} }}
> >
{component.label} {component.label}
{component.required && <span style={{color: "#ef4444", {component.required && (
<span
style={{
color: "#ef4444",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}), ...(isInteractive && component.style ? component.style : {}),
}}>*</span>} }}
>
*
</span>
)}
</label> </label>
)} )}
<label <label
style={{display: "flex", style={{
display: "flex",
alignItems: "center", alignItems: "center",
gap: "12px", gap: "12px",
cursor: "pointer", cursor: "pointer",
@ -114,7 +122,8 @@ export const ToggleSwitchComponent: React.FC<ToggleSwitchComponentProps> = ({
onDragEnd={onDragEnd} onDragEnd={onDragEnd}
> >
<div <div
style={{position: "relative", style={{
position: "relative",
width: "48px", width: "48px",
height: "24px", height: "24px",
backgroundColor: component.value === true ? "#3b82f6" : "#d1d5db", backgroundColor: component.value === true ? "#3b82f6" : "#d1d5db",
@ -162,10 +171,15 @@ export const ToggleSwitchComponent: React.FC<ToggleSwitchComponentProps> = ({
}} }}
/> />
</div> </div>
<span style={{color: "#374151", <span
style={{
color: "#374151",
// isInteractive 모드에서는 사용자 스타일 우선 적용 // isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}), ...(isInteractive && component.style ? component.style : {}),
}}>{componentConfig.toggleLabel || (component.value ? "켜짐" : "꺼짐")}</span> }}
>
{componentConfig.toggleLabel || (component.value ? "켜짐" : "꺼짐")}
</span>
</label> </label>
</div> </div>
); );