라벨표시 수정

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="h-full w-full">
{/* 라벨 표시 */}
{!hideLabel && component.label && (
{/* 라벨 표시 - 컴포넌트 내부에서 라벨을 처리하므로 외부에서는 표시하지 않음 */}
{!hideLabel && component.label && component.style?.labelDisplay === false && (
<div className="mb-1">
<label className="text-sm font-medium text-gray-700">
{component.label}

View File

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

View File

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

View File

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

View File

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

View File

@ -77,7 +77,7 @@ export const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
return (
<div style={componentStyle} className={className} {...domProps}>
{/* 라벨 렌더링 */}
{component.label && (
{component.label && component.style?.labelDisplay !== false && (
<label
style={{
position: "absolute",
@ -91,15 +91,23 @@ export const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
}}
>
{component.label}
{component.required && <span style={{color: "#ef4444",
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}>*</span>}
{component.required && (
<span
style={{
color: "#ef4444",
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}
>
*
</span>
)}
</label>
)}
<div
style={{width: "100%",
style={{
width: "100%",
height: "100%",
border: "2px dashed #d1d5db",
borderRadius: "8px",
@ -110,9 +118,9 @@ export const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
cursor: "pointer",
backgroundColor: "#f9fafb",
position: "relative",
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}
onClick={handleClick}
onDragStart={onDragStart}
onDragEnd={onDragEnd}
@ -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 && component.style ? component.style : {}),
}}>
<div style={{fontSize: "24px", marginBottom: "8px",
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}>📁</div>
<div style={{fontWeight: "500",
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}> </div>
<div style={{fontSize: "12px", marginTop: "4px",
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}>
}}
>
<div
style={{
fontSize: "24px",
marginBottom: "8px",
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}
>
📁
</div>
<div
style={{
fontWeight: "500",
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}
>
</div>
<div
style={{
fontSize: "12px",
marginTop: "4px",
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}
>
{componentConfig.accept && `지원 형식: ${componentConfig.accept}`}
</div>
</div>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -77,7 +77,7 @@ export const ToggleSwitchComponent: React.FC<ToggleSwitchComponentProps> = ({
return (
<div style={componentStyle} className={className} {...domProps}>
{/* 라벨 렌더링 */}
{component.label && (
{component.label && component.style?.labelDisplay !== false && (
<label
style={{
position: "absolute",
@ -91,30 +91,39 @@ export const ToggleSwitchComponent: React.FC<ToggleSwitchComponentProps> = ({
}}
>
{component.label}
{component.required && <span style={{color: "#ef4444",
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}>*</span>}
{component.required && (
<span
style={{
color: "#ef4444",
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}
>
*
</span>
)}
</label>
)}
<label
style={{display: "flex",
style={{
display: "flex",
alignItems: "center",
gap: "12px",
cursor: "pointer",
width: "100%",
height: "100%",
fontSize: "14px",
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}
onClick={handleClick}
onDragStart={onDragStart}
onDragEnd={onDragEnd}
>
<div
style={{position: "relative",
style={{
position: "relative",
width: "48px",
height: "24px",
backgroundColor: component.value === true ? "#3b82f6" : "#d1d5db",
@ -132,14 +141,14 @@ export const ToggleSwitchComponent: React.FC<ToggleSwitchComponentProps> = ({
disabled={componentConfig.disabled || false}
required={componentConfig.required || false}
style={{
position: "absolute",
position: "absolute",
opacity: 0,
width: "100%",
height: "100%",
cursor: "pointer",
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}
onChange={(e) => {
if (component.onChange) {
component.onChange(e.target.checked);
@ -148,7 +157,7 @@ export const ToggleSwitchComponent: React.FC<ToggleSwitchComponentProps> = ({
/>
<div
style={{
position: "absolute",
position: "absolute",
top: "2px",
left: component.value === true ? "26px" : "2px",
width: "20px",
@ -157,15 +166,20 @@ export const ToggleSwitchComponent: React.FC<ToggleSwitchComponentProps> = ({
borderRadius: "50%",
transition: "left 0.2s",
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)",
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}
/>
</div>
<span
style={{
color: "#374151",
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}
/>
</div>
<span style={{color: "#374151",
// isInteractive 모드에서는 사용자 스타일 우선 적용
...(isInteractive && component.style ? component.style : {}),
}}>{componentConfig.toggleLabel || (component.value ? "켜짐" : "꺼짐")}</span>
>
{componentConfig.toggleLabel || (component.value ? "켜짐" : "꺼짐")}
</span>
</label>
</div>
);