Files
vexplor/frontend/lib/registry/components/image-display/ImageDisplayComponent.tsx
kjs 6901baab8e feat(screen-designer): 그리드 컬럼 시스템 개선 및 컴포넌트 너비 렌더링 수정
주요 변경사항:
- 격자 설정을 편집 탭에서 항상 표시 (해상도 설정 하단)
- 그리드 컬럼 수 동적 조정 가능 (1-24)
- 컴포넌트 생성 시 현재 그리드 컬럼 수 기반 자동 계산
- 컴포넌트 너비가 설정한 컬럼 수대로 정확히 표시되도록 수정

수정된 파일:
- ScreenDesigner: 컴포넌트 드롭 시 gridColumns와 style.width 동적 계산
- UnifiedPropertiesPanel: 격자 설정 UI 통합, 차지 컬럼 수 설정 시 width 자동 계산
- RealtimePreviewDynamic: getWidth 우선순위 수정, DOM 크기 디버깅 로그 추가
- 8개 컴포넌트: componentStyle.width를 항상 100%로 고정
  * ButtonPrimaryComponent
  * TextInputComponent
  * NumberInputComponent
  * TextareaBasicComponent
  * DateInputComponent
  * TableListComponent
  * CardDisplayComponent

문제 해결:
- 컴포넌트 내부에서 component.style.width를 재사용하여 이중 축소 발생
- 해결: 부모 컨테이너(RealtimePreviewDynamic)가 width 제어, 컴포넌트는 항상 100%
- 결과: 파란 테두리와 내부 콘텐츠가 동일한 크기로 정확히 표시
2025-11-04 16:17:19 +09:00

169 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import React from "react";
import { ComponentRendererProps } from "@/types/component";
import { ImageDisplayConfig } from "./types";
export interface ImageDisplayComponentProps extends ComponentRendererProps {
config?: ImageDisplayConfig;
}
/**
* ImageDisplay 컴포넌트
* image-display 컴포넌트입니다
*/
export const ImageDisplayComponent: React.FC<ImageDisplayComponentProps> = ({
component,
isDesignMode = false,
isSelected = false,
onClick,
onDragStart,
onDragEnd,
config,
className,
style,
...props
}) => {
// 컴포넌트 설정
const componentConfig = {
...config,
...component.config,
} as ImageDisplayConfig;
// 스타일 계산 (위치는 RealtimePreviewDynamic에서 처리하므로 제외)
const componentStyle: React.CSSProperties = {
width: "100%",
height: "100%",
...component.style,
...style,
// width는 항상 100%로 고정 (부모 컨테이너가 gridColumns로 크기 제어)
width: "100%",
};
// 디자인 모드 스타일
if (isDesignMode) {
componentStyle.border = "1px dashed #cbd5e1";
componentStyle.borderColor = isSelected ? "#3b82f6" : "#cbd5e1";
}
// 이벤트 핸들러
const handleClick = (e: React.MouseEvent) => {
e.stopPropagation();
onClick?.();
};
// DOM에 전달하면 안 되는 React-specific props 필터링
const {
selectedScreen,
onZoneComponentDrop,
onZoneClick,
componentConfig: _componentConfig,
component: _component,
isSelected: _isSelected,
onClick: _onClick,
onDragStart: _onDragStart,
onDragEnd: _onDragEnd,
size: _size,
position: _position,
style: _style,
screenId: _screenId,
tableName: _tableName,
onRefresh: _onRefresh,
onClose: _onClose,
...domProps
} = props;
return (
<div style={componentStyle} className={className} {...domProps}>
{/* 라벨 렌더링 */}
{component.label && (component.style?.labelDisplay ?? true) && (
<label
style={{
position: "absolute",
top: "-25px",
left: "0px",
fontSize: component.style?.labelFontSize || "14px",
color: component.style?.labelColor || "#64748b",
fontWeight: "500",
}}
>
{component.label}
{component.required && <span style={{ color: "#ef4444" }}>*</span>}
</label>
)}
<div
style={{
width: "100%",
height: "100%",
border: "1px solid #d1d5db",
borderRadius: "8px",
overflow: "hidden",
display: "flex",
alignItems: "center",
justifyContent: "center",
backgroundColor: "#f9fafb",
transition: "all 0.2s ease-in-out",
boxShadow: "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
}}
onMouseEnter={(e) => {
e.currentTarget.style.borderColor = "#f97316";
e.currentTarget.style.boxShadow = "0 4px 6px -1px rgba(0, 0, 0, 0.1)";
}}
onMouseLeave={(e) => {
e.currentTarget.style.borderColor = "#d1d5db";
e.currentTarget.style.boxShadow = "0 1px 2px 0 rgba(0, 0, 0, 0.05)";
}}
onClick={handleClick}
onDragStart={onDragStart}
onDragEnd={onDragEnd}
>
{component.value || componentConfig.imageUrl ? (
<img
src={component.value || componentConfig.imageUrl}
alt={componentConfig.altText || "이미지"}
style={{
maxWidth: "100%",
maxHeight: "100%",
objectFit: componentConfig.objectFit || "contain",
}}
onError={(e) => {
(e.target as HTMLImageElement).style.display = "none";
if (e.target?.parentElement) {
e.target.parentElement.innerHTML = `
<div style="display: flex; flex-direction: column; align-items: center; gap: 8px; color: #6b7280; font-size: 14px;">
<div style="font-size: 24px;">🖼️</div>
<div>이미지 로드 실패</div>
</div>
`;
}
}}
/>
) : (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "8px",
color: "#6b7280",
fontSize: "14px",
}}
>
<div style={{ fontSize: "32px" }}>🖼</div>
<div> </div>
</div>
)}
</div>
</div>
);
};
/**
* ImageDisplay 래퍼 컴포넌트
* 추가적인 로직이나 상태 관리가 필요한 경우 사용
*/
export const ImageDisplayWrapper: React.FC<ImageDisplayComponentProps> = (props) => {
return <ImageDisplayComponent {...props} />;
};