feat: ScreenModal 및 V2 컴포넌트 레이아웃 개선
- ScreenModal 컴포넌트의 모달 크기 조정을 위해 헤더 및 푸터 높이를 수정하고, 여백을 최소화하여 디자인 일치를 도모하였습니다. - V2 컴포넌트에서 라벨 높이를 계산하여 절대 위치로 배치하도록 변경하였으며, 입력 필드 및 선택 컴포넌트의 구조를 개선하여 일관된 사용자 경험을 제공하였습니다. - 플레이스홀더 기능을 추가하여 날짜 선택 시 사용자 편의성을 높였습니다. - 관련 CSS 클래스를 업데이트하여 반응형 디자인을 강화하였습니다.
This commit is contained in:
parent
faf4f566f7
commit
942eb079e8
|
|
@ -9,6 +9,7 @@ services:
|
|||
- "9771:3000"
|
||||
environment:
|
||||
- NEXT_PUBLIC_API_URL=http://localhost:8080/api
|
||||
- NODE_OPTIONS=--max-old-space-size=4096
|
||||
volumes:
|
||||
- ../../frontend:/app
|
||||
- /app/node_modules
|
||||
|
|
|
|||
|
|
@ -504,18 +504,18 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
|
|||
}
|
||||
|
||||
// 화면관리에서 설정한 크기 = 컨텐츠 영역 크기
|
||||
// 실제 모달 크기 = 컨텐츠 + 헤더 + 연속등록 체크박스 + gap + padding
|
||||
const headerHeight = 52; // DialogHeader (타이틀 + border-b + py-3)
|
||||
const footerHeight = 52; // 연속 등록 모드 체크박스 영역
|
||||
const dialogGap = 16; // DialogContent gap-4
|
||||
const extraPadding = 24; // 추가 여백 (안전 마진)
|
||||
// 실제 모달 크기 = 컨텐츠 + 헤더 + 연속등록 체크박스
|
||||
// 🔧 여백 최소화: 디자이너와 일치하도록 조정
|
||||
const headerHeight = 48; // DialogHeader (타이틀 + border-b + py-3)
|
||||
const footerHeight = 44; // 연속 등록 모드 체크박스 영역
|
||||
const horizontalPadding = 16; // 좌우 패딩 최소화
|
||||
|
||||
const totalHeight = screenDimensions.height + headerHeight + footerHeight + dialogGap + extraPadding;
|
||||
const totalHeight = screenDimensions.height + headerHeight + footerHeight;
|
||||
|
||||
return {
|
||||
className: "overflow-hidden p-0",
|
||||
style: {
|
||||
width: `${Math.min(screenDimensions.width + 48, window.innerWidth * 0.98)}px`, // 좌우 패딩 추가
|
||||
width: `${Math.min(screenDimensions.width + horizontalPadding, window.innerWidth * 0.98)}px`,
|
||||
height: `${Math.min(totalHeight, window.innerHeight * 0.95)}px`,
|
||||
maxWidth: "98vw",
|
||||
maxHeight: "95vh",
|
||||
|
|
@ -587,7 +587,7 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
|
|||
return (
|
||||
<Dialog open={modalState.isOpen} onOpenChange={handleClose}>
|
||||
<DialogContent
|
||||
className={`${modalStyle.className} ${className || ""} max-w-none`}
|
||||
className={`${modalStyle.className} ${className || ""} max-w-none flex flex-col`}
|
||||
{...(modalStyle.style && { style: modalStyle.style })}
|
||||
>
|
||||
<DialogHeader className="shrink-0 border-b px-4 py-3">
|
||||
|
|
@ -602,7 +602,9 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
|
|||
</div>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="flex-1 overflow-y-auto [&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-gray-300 [&::-webkit-scrollbar-track]:bg-transparent">
|
||||
<div
|
||||
className="flex-1 overflow-hidden flex items-center justify-center"
|
||||
>
|
||||
{loading ? (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<div className="text-center">
|
||||
|
|
@ -614,11 +616,10 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
|
|||
<ActiveTabProvider>
|
||||
<TableOptionsProvider>
|
||||
<div
|
||||
className="relative mx-auto bg-white"
|
||||
className="relative bg-white"
|
||||
style={{
|
||||
width: `${screenDimensions?.width || 800}px`,
|
||||
height: `${screenDimensions?.height || 600}px`,
|
||||
transformOrigin: "center center",
|
||||
}}
|
||||
>
|
||||
{screenData.components.map((component) => {
|
||||
|
|
|
|||
|
|
@ -598,12 +598,7 @@ const RealtimePreviewDynamicComponent: React.FC<RealtimePreviewProps> = ({
|
|||
(contentRef as any).current = node;
|
||||
}
|
||||
}}
|
||||
className={`${
|
||||
(component.type === "component" && (component as any).componentType === "flow-widget") ||
|
||||
((component as any).componentType === "conditional-container" && !isDesignMode)
|
||||
? "h-auto"
|
||||
: "h-full"
|
||||
} overflow-visible`}
|
||||
className="h-full overflow-visible"
|
||||
style={{ width: "100%", maxWidth: "100%" }}
|
||||
>
|
||||
<DynamicComponentRenderer
|
||||
|
|
|
|||
|
|
@ -311,36 +311,39 @@ export const V2Biz = forwardRef<HTMLDivElement, V2BizProps>(
|
|||
const componentWidth = size?.width || style?.width;
|
||||
const componentHeight = size?.height || style?.height;
|
||||
|
||||
// 라벨 높이 계산 (기본 20px, 사용자 설정에 따라 조정)
|
||||
const labelFontSize = style?.labelFontSize ? parseInt(String(style.labelFontSize)) : 14;
|
||||
const labelMarginBottom = style?.labelMarginBottom ? parseInt(String(style.labelMarginBottom)) : 4;
|
||||
const estimatedLabelHeight = labelFontSize + labelMarginBottom + 2;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
id={id}
|
||||
className="flex flex-col"
|
||||
className="relative"
|
||||
style={{
|
||||
width: componentWidth,
|
||||
// 🔧 높이는 컨테이너가 아닌 입력 필드에만 적용 (라벨 높이는 별도)
|
||||
height: componentHeight,
|
||||
}}
|
||||
>
|
||||
{/* 🔧 라벨을 absolute로 컴포넌트 위에 배치 */}
|
||||
{showLabel && (
|
||||
<Label
|
||||
htmlFor={id}
|
||||
style={{
|
||||
fontSize: style?.labelFontSize,
|
||||
color: style?.labelColor,
|
||||
fontWeight: style?.labelFontWeight,
|
||||
marginBottom: style?.labelMarginBottom,
|
||||
position: "absolute",
|
||||
top: `-${estimatedLabelHeight}px`,
|
||||
left: 0,
|
||||
fontSize: style?.labelFontSize || "14px",
|
||||
color: style?.labelColor || "#64748b",
|
||||
fontWeight: style?.labelFontWeight || "500",
|
||||
}}
|
||||
className="text-sm font-medium flex-shrink-0"
|
||||
className="text-sm font-medium whitespace-nowrap"
|
||||
>
|
||||
{label}
|
||||
</Label>
|
||||
)}
|
||||
<div
|
||||
className="min-h-0"
|
||||
style={{
|
||||
height: componentHeight,
|
||||
}}
|
||||
>
|
||||
<div className="h-full w-full">
|
||||
{renderBiz()}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -75,10 +75,11 @@ const SingleDatePicker = forwardRef<
|
|||
disabled?: boolean;
|
||||
readonly?: boolean;
|
||||
className?: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
>(
|
||||
(
|
||||
{ value, onChange, dateFormat = "YYYY-MM-DD", showToday = true, minDate, maxDate, disabled, readonly, className },
|
||||
{ value, onChange, dateFormat = "YYYY-MM-DD", showToday = true, minDate, maxDate, disabled, readonly, className, placeholder = "날짜 선택" },
|
||||
ref,
|
||||
) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
|
@ -87,6 +88,16 @@ const SingleDatePicker = forwardRef<
|
|||
const minDateObj = useMemo(() => parseDate(minDate, dateFormat), [minDate, dateFormat]);
|
||||
const maxDateObj = useMemo(() => parseDate(maxDate, dateFormat), [maxDate, dateFormat]);
|
||||
|
||||
// 표시할 날짜 텍스트 계산 (ISO 형식이면 포맷팅, 아니면 그대로)
|
||||
const displayText = useMemo(() => {
|
||||
if (!value) return "";
|
||||
// Date 객체로 변환 후 포맷팅
|
||||
if (date && isValid(date)) {
|
||||
return formatDate(date, dateFormat);
|
||||
}
|
||||
return value;
|
||||
}, [value, date, dateFormat]);
|
||||
|
||||
const handleSelect = useCallback(
|
||||
(selectedDate: Date | undefined) => {
|
||||
if (selectedDate) {
|
||||
|
|
@ -115,13 +126,13 @@ const SingleDatePicker = forwardRef<
|
|||
variant="outline"
|
||||
disabled={disabled || readonly}
|
||||
className={cn(
|
||||
"h-10 w-full justify-start text-left font-normal",
|
||||
!value && "text-muted-foreground",
|
||||
"h-full w-full justify-start text-left font-normal",
|
||||
!displayText && "text-muted-foreground",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{value || "날짜 선택"}
|
||||
<CalendarIcon className="mr-2 h-4 w-4 flex-shrink-0" />
|
||||
{displayText || placeholder}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
|
|
@ -409,6 +420,7 @@ export const V2Date = forwardRef<HTMLDivElement, V2DateProps>((props, ref) => {
|
|||
maxDate={config.maxDate}
|
||||
disabled={isDisabled}
|
||||
readonly={readonly}
|
||||
placeholder={config.placeholder}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
@ -444,6 +456,7 @@ export const V2Date = forwardRef<HTMLDivElement, V2DateProps>((props, ref) => {
|
|||
showToday={config.showToday}
|
||||
disabled={isDisabled}
|
||||
readonly={readonly}
|
||||
placeholder={config.placeholder}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -453,37 +466,40 @@ export const V2Date = forwardRef<HTMLDivElement, V2DateProps>((props, ref) => {
|
|||
const componentWidth = size?.width || style?.width;
|
||||
const componentHeight = size?.height || style?.height;
|
||||
|
||||
// 라벨 높이 계산 (기본 20px, 사용자 설정에 따라 조정)
|
||||
const labelFontSize = style?.labelFontSize ? parseInt(String(style.labelFontSize)) : 14;
|
||||
const labelMarginBottom = style?.labelMarginBottom ? parseInt(String(style.labelMarginBottom)) : 4;
|
||||
const estimatedLabelHeight = labelFontSize + labelMarginBottom + 2;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
id={id}
|
||||
className="flex flex-col"
|
||||
className="relative"
|
||||
style={{
|
||||
width: componentWidth,
|
||||
// 🔧 높이는 컨테이너가 아닌 입력 필드에만 적용 (라벨 높이는 별도)
|
||||
height: componentHeight,
|
||||
}}
|
||||
>
|
||||
{/* 🔧 라벨을 absolute로 컴포넌트 위에 배치 */}
|
||||
{showLabel && (
|
||||
<Label
|
||||
htmlFor={id}
|
||||
style={{
|
||||
fontSize: style?.labelFontSize,
|
||||
color: style?.labelColor,
|
||||
fontWeight: style?.labelFontWeight,
|
||||
marginBottom: style?.labelMarginBottom,
|
||||
position: "absolute",
|
||||
top: `-${estimatedLabelHeight}px`,
|
||||
left: 0,
|
||||
fontSize: style?.labelFontSize || "14px",
|
||||
color: style?.labelColor || "#64748b",
|
||||
fontWeight: style?.labelFontWeight || "500",
|
||||
}}
|
||||
className="flex-shrink-0 text-sm font-medium"
|
||||
className="text-sm font-medium whitespace-nowrap"
|
||||
>
|
||||
{label}
|
||||
{required && <span className="ml-0.5 text-orange-500">*</span>}
|
||||
</Label>
|
||||
)}
|
||||
<div
|
||||
className="min-h-0"
|
||||
style={{
|
||||
height: componentHeight,
|
||||
}}
|
||||
>
|
||||
<div className="h-full w-full">
|
||||
{renderDatePicker()}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -462,37 +462,40 @@ export const V2Hierarchy = forwardRef<HTMLDivElement, V2HierarchyProps>(
|
|||
const componentWidth = size?.width || style?.width;
|
||||
const componentHeight = size?.height || style?.height;
|
||||
|
||||
// 라벨 높이 계산 (기본 20px, 사용자 설정에 따라 조정)
|
||||
const labelFontSize = style?.labelFontSize ? parseInt(String(style.labelFontSize)) : 14;
|
||||
const labelMarginBottom = style?.labelMarginBottom ? parseInt(String(style.labelMarginBottom)) : 4;
|
||||
const estimatedLabelHeight = labelFontSize + labelMarginBottom + 2;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
id={id}
|
||||
className="flex flex-col"
|
||||
className="relative"
|
||||
style={{
|
||||
width: componentWidth,
|
||||
// 🔧 높이는 컨테이너가 아닌 입력 필드에만 적용 (라벨 높이는 별도)
|
||||
height: componentHeight,
|
||||
}}
|
||||
>
|
||||
{/* 🔧 라벨을 absolute로 컴포넌트 위에 배치 */}
|
||||
{showLabel && (
|
||||
<Label
|
||||
htmlFor={id}
|
||||
style={{
|
||||
fontSize: style?.labelFontSize,
|
||||
color: style?.labelColor,
|
||||
fontWeight: style?.labelFontWeight,
|
||||
marginBottom: style?.labelMarginBottom,
|
||||
position: "absolute",
|
||||
top: `-${estimatedLabelHeight}px`,
|
||||
left: 0,
|
||||
fontSize: style?.labelFontSize || "14px",
|
||||
color: style?.labelColor || "#64748b",
|
||||
fontWeight: style?.labelFontWeight || "500",
|
||||
}}
|
||||
className="text-sm font-medium flex-shrink-0"
|
||||
className="text-sm font-medium whitespace-nowrap"
|
||||
>
|
||||
{label}
|
||||
{required && <span className="text-orange-500 ml-0.5">*</span>}
|
||||
</Label>
|
||||
)}
|
||||
<div
|
||||
className="min-h-0"
|
||||
style={{
|
||||
height: componentHeight,
|
||||
}}
|
||||
>
|
||||
<div className="h-full w-full">
|
||||
{renderHierarchy()}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -792,37 +792,40 @@ export const V2Input = forwardRef<HTMLDivElement, V2InputProps>((props, ref) =>
|
|||
const componentWidth = size?.width || style?.width;
|
||||
const componentHeight = size?.height || style?.height;
|
||||
|
||||
// 라벨 높이 계산 (기본 20px, 사용자 설정에 따라 조정)
|
||||
const labelFontSize = style?.labelFontSize ? parseInt(String(style.labelFontSize)) : 14;
|
||||
const labelMarginBottom = style?.labelMarginBottom ? parseInt(String(style.labelMarginBottom)) : 4;
|
||||
const estimatedLabelHeight = labelFontSize + labelMarginBottom + 2; // 라벨 높이 + 여백
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
id={id}
|
||||
className="flex flex-col"
|
||||
className="relative"
|
||||
style={{
|
||||
width: componentWidth,
|
||||
// 🔧 높이는 컨테이너가 아닌 입력 필드에만 적용 (라벨 높이는 별도)
|
||||
height: componentHeight,
|
||||
}}
|
||||
>
|
||||
{/* 🔧 라벨을 absolute로 컴포넌트 위에 배치 (높이에 포함되지 않음) */}
|
||||
{showLabel && (
|
||||
<Label
|
||||
htmlFor={id}
|
||||
style={{
|
||||
fontSize: style?.labelFontSize,
|
||||
color: style?.labelColor,
|
||||
fontWeight: style?.labelFontWeight,
|
||||
marginBottom: style?.labelMarginBottom,
|
||||
position: "absolute",
|
||||
top: `-${estimatedLabelHeight}px`,
|
||||
left: 0,
|
||||
fontSize: style?.labelFontSize || "14px",
|
||||
color: style?.labelColor || "#64748b",
|
||||
fontWeight: style?.labelFontWeight || "500",
|
||||
}}
|
||||
className="flex-shrink-0 text-sm font-medium"
|
||||
className="text-sm font-medium whitespace-nowrap"
|
||||
>
|
||||
{label}
|
||||
{required && <span className="ml-0.5 text-orange-500">*</span>}
|
||||
</Label>
|
||||
)}
|
||||
<div
|
||||
className="min-h-0"
|
||||
style={{
|
||||
height: componentHeight,
|
||||
}}
|
||||
>
|
||||
<div className="h-full w-full">
|
||||
{renderInput()}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -536,37 +536,40 @@ export const V2Media = forwardRef<HTMLDivElement, V2MediaProps>(
|
|||
const componentWidth = size?.width || style?.width;
|
||||
const componentHeight = size?.height || style?.height;
|
||||
|
||||
// 라벨 높이 계산 (기본 20px, 사용자 설정에 따라 조정)
|
||||
const labelFontSize = style?.labelFontSize ? parseInt(String(style.labelFontSize)) : 14;
|
||||
const labelMarginBottom = style?.labelMarginBottom ? parseInt(String(style.labelMarginBottom)) : 4;
|
||||
const estimatedLabelHeight = labelFontSize + labelMarginBottom + 2;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
id={id}
|
||||
className="flex w-full flex-col"
|
||||
className="relative w-full"
|
||||
style={{
|
||||
width: componentWidth,
|
||||
// 🔧 높이는 컨테이너가 아닌 컨텐츠 영역에만 적용 (라벨 높이는 별도)
|
||||
height: componentHeight,
|
||||
}}
|
||||
>
|
||||
{/* 🔧 라벨을 absolute로 컴포넌트 위에 배치 */}
|
||||
{showLabel && (
|
||||
<Label
|
||||
htmlFor={id}
|
||||
style={{
|
||||
fontSize: style?.labelFontSize,
|
||||
color: style?.labelColor,
|
||||
fontWeight: style?.labelFontWeight,
|
||||
marginBottom: style?.labelMarginBottom,
|
||||
position: "absolute",
|
||||
top: `-${estimatedLabelHeight}px`,
|
||||
left: 0,
|
||||
fontSize: style?.labelFontSize || "14px",
|
||||
color: style?.labelColor || "#64748b",
|
||||
fontWeight: style?.labelFontWeight || "500",
|
||||
}}
|
||||
className="text-sm font-medium flex-shrink-0"
|
||||
className="text-sm font-medium whitespace-nowrap"
|
||||
>
|
||||
{label}
|
||||
{required && <span className="text-orange-500 ml-0.5">*</span>}
|
||||
</Label>
|
||||
)}
|
||||
<div
|
||||
className="min-h-0"
|
||||
style={{
|
||||
height: componentHeight,
|
||||
}}
|
||||
>
|
||||
<div className="h-full w-full">
|
||||
{renderMedia()}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -744,37 +744,40 @@ export const V2Select = forwardRef<HTMLDivElement, V2SelectProps>(
|
|||
const componentWidth = size?.width || style?.width;
|
||||
const componentHeight = size?.height || style?.height;
|
||||
|
||||
// 라벨 높이 계산 (기본 20px, 사용자 설정에 따라 조정)
|
||||
const labelFontSize = style?.labelFontSize ? parseInt(String(style.labelFontSize)) : 14;
|
||||
const labelMarginBottom = style?.labelMarginBottom ? parseInt(String(style.labelMarginBottom)) : 4;
|
||||
const estimatedLabelHeight = labelFontSize + labelMarginBottom + 2;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
id={id}
|
||||
className="flex flex-col"
|
||||
className="relative"
|
||||
style={{
|
||||
width: componentWidth,
|
||||
// 🔧 높이는 컨테이너가 아닌 입력 필드에만 적용 (라벨 높이는 별도)
|
||||
height: componentHeight,
|
||||
}}
|
||||
>
|
||||
{/* 🔧 라벨을 absolute로 컴포넌트 위에 배치 */}
|
||||
{showLabel && (
|
||||
<Label
|
||||
htmlFor={id}
|
||||
style={{
|
||||
fontSize: style?.labelFontSize,
|
||||
color: style?.labelColor,
|
||||
fontWeight: style?.labelFontWeight,
|
||||
marginBottom: style?.labelMarginBottom,
|
||||
position: "absolute",
|
||||
top: `-${estimatedLabelHeight}px`,
|
||||
left: 0,
|
||||
fontSize: style?.labelFontSize || "14px",
|
||||
color: style?.labelColor || "#64748b",
|
||||
fontWeight: style?.labelFontWeight || "500",
|
||||
}}
|
||||
className="text-sm font-medium flex-shrink-0"
|
||||
className="text-sm font-medium whitespace-nowrap"
|
||||
>
|
||||
{label}
|
||||
{required && <span className="text-orange-500 ml-0.5">*</span>}
|
||||
</Label>
|
||||
)}
|
||||
<div
|
||||
className="min-h-0"
|
||||
style={{
|
||||
height: componentHeight,
|
||||
}}
|
||||
>
|
||||
<div className="h-full w-full">
|
||||
{renderSelect()}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -48,6 +48,20 @@ export const V2DateConfigPanel: React.FC<V2DateConfigPanelProps> = ({
|
|||
|
||||
<Separator />
|
||||
|
||||
{/* 플레이스홀더 */}
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-medium">플레이스홀더</Label>
|
||||
<Input
|
||||
value={config.placeholder || ""}
|
||||
onChange={(e) => updateConfig("placeholder", e.target.value)}
|
||||
placeholder="날짜 선택"
|
||||
className="h-8 text-xs"
|
||||
/>
|
||||
<p className="text-muted-foreground text-[10px]">날짜가 선택되지 않았을 때 표시할 텍스트</p>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
{/* 표시 형식 */}
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-medium">표시 형식</Label>
|
||||
|
|
|
|||
|
|
@ -29,10 +29,15 @@ export class V2DateRenderer extends AutoRegisteringComponentRenderer {
|
|||
}
|
||||
};
|
||||
|
||||
// 라벨: style.labelText 우선, 없으면 component.label 사용
|
||||
// style.labelDisplay가 false면 라벨 숨김
|
||||
const style = component.style || {};
|
||||
const effectiveLabel = style.labelDisplay === false ? undefined : (style.labelText || component.label);
|
||||
|
||||
return (
|
||||
<V2Date
|
||||
id={component.id}
|
||||
label={component.label}
|
||||
label={effectiveLabel}
|
||||
required={component.required}
|
||||
readonly={config.readonly || component.readonly}
|
||||
disabled={config.disabled || component.disabled}
|
||||
|
|
@ -41,7 +46,7 @@ export class V2DateRenderer extends AutoRegisteringComponentRenderer {
|
|||
config={{
|
||||
dateType: config.dateType || config.webType || "date",
|
||||
format: config.format || "YYYY-MM-DD",
|
||||
placeholder: config.placeholder || "날짜 선택",
|
||||
placeholder: config.placeholder || style.placeholder || "날짜 선택",
|
||||
showTime: config.showTime || false,
|
||||
use24Hours: config.use24Hours ?? true,
|
||||
minDate: config.minDate,
|
||||
|
|
|
|||
|
|
@ -84,23 +84,7 @@ export const TextDisplayComponent: React.FC<TextDisplayComponentProps> = ({
|
|||
|
||||
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>
|
||||
)}
|
||||
|
||||
{/* v2-text-display는 텍스트 표시 전용이므로 별도 라벨 불필요 */}
|
||||
<div style={textStyle} onClick={handleClick} onDragStart={onDragStart} onDragEnd={onDragEnd}>
|
||||
{componentConfig.text || "텍스트를 입력하세요"}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue