feat: ScreenModal 및 V2Select 컴포넌트 개선
- ScreenModal에서 모달 크기 계산 로직을 개선하여, 콘텐츠가 화면 높이를 초과할 때만 스크롤이 필요하도록 수정하였습니다. - V2Select 및 관련 컴포넌트에서 height 및 style props를 추가하여, 사용자 정의 스타일을 보다 효과적으로 적용할 수 있도록 하였습니다. - DropdownSelect에서 height 스타일을 직접 전달하여, 다양한 높이 설정을 지원하도록 개선하였습니다. - CategorySelectComponent에서 라벨 표시 및 높이 계산 로직을 추가하여, 사용자 경험을 향상시켰습니다.
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { getCategoryValues } from "@/lib/api/tableCategoryValue";
|
||||
import { TableCategoryValue } from "@/types/tableCategoryValue";
|
||||
import { Loader2 } from "lucide-react";
|
||||
@@ -23,6 +24,20 @@ interface CategorySelectComponentProps {
|
||||
readonly?: boolean;
|
||||
tableName?: string;
|
||||
columnName?: string;
|
||||
// 🔧 높이 조절을 위한 props 추가
|
||||
style?: React.CSSProperties & {
|
||||
labelDisplay?: boolean;
|
||||
labelFontSize?: string | number;
|
||||
labelColor?: string;
|
||||
labelFontWeight?: string | number;
|
||||
labelMarginBottom?: string | number;
|
||||
};
|
||||
size?: { width?: number | string; height?: number | string };
|
||||
// 🔧 라벨 표시를 위한 props 추가
|
||||
label?: string;
|
||||
id?: string;
|
||||
// 🔧 디자인 모드 처리
|
||||
isDesignMode?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,7 +58,27 @@ export const CategorySelectComponent: React.FC<
|
||||
readonly = false,
|
||||
tableName: propTableName,
|
||||
columnName: propColumnName,
|
||||
style,
|
||||
size,
|
||||
label: propLabel,
|
||||
id: propId,
|
||||
isDesignMode = false,
|
||||
}) => {
|
||||
// 🔧 높이 계산: size.height > style.height > 기본값(40px)
|
||||
const componentHeight = size?.height || style?.height;
|
||||
const heightStyle: React.CSSProperties = componentHeight
|
||||
? { height: componentHeight }
|
||||
: {};
|
||||
|
||||
// 🔧 라벨 관련 계산
|
||||
const label = propLabel || component?.label;
|
||||
const id = propId || component?.id;
|
||||
const showLabel = label && style?.labelDisplay !== false;
|
||||
|
||||
// 라벨 높이 계산 (기본 20px)
|
||||
const labelFontSize = style?.labelFontSize ? parseInt(String(style.labelFontSize)) : 14;
|
||||
const labelMarginBottom = style?.labelMarginBottom ? parseInt(String(style.labelMarginBottom)) : 4;
|
||||
const estimatedLabelHeight = labelFontSize + labelMarginBottom + 2;
|
||||
const [categoryValues, setCategoryValues] = useState<TableCategoryValue[]>(
|
||||
[]
|
||||
);
|
||||
@@ -97,12 +132,49 @@ export const CategorySelectComponent: React.FC<
|
||||
onChange?.(newValue);
|
||||
};
|
||||
|
||||
// 🔧 공통 라벨 렌더링 함수
|
||||
const renderLabel = () => {
|
||||
if (!showLabel) return null;
|
||||
return (
|
||||
<Label
|
||||
htmlFor={id}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: `-${estimatedLabelHeight}px`,
|
||||
left: 0,
|
||||
fontSize: style?.labelFontSize || "14px",
|
||||
color: style?.labelColor || "#64748b",
|
||||
fontWeight: style?.labelFontWeight || "500",
|
||||
}}
|
||||
className="text-sm font-medium whitespace-nowrap"
|
||||
>
|
||||
{label}
|
||||
{required && <span className="text-orange-500 ml-0.5">*</span>}
|
||||
</Label>
|
||||
);
|
||||
};
|
||||
|
||||
// 🔧 공통 wrapper 스타일
|
||||
const wrapperStyle: React.CSSProperties = {
|
||||
width: size?.width || style?.width,
|
||||
height: componentHeight,
|
||||
};
|
||||
|
||||
// 🔧 디자인 모드일 때 클릭 방지
|
||||
const designModeClass = isDesignMode ? "pointer-events-none" : "";
|
||||
|
||||
// 로딩 중
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex h-10 w-full items-center justify-center rounded-md border bg-background px-3 text-sm text-muted-foreground">
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
로딩 중...
|
||||
<div id={id} className={`relative ${designModeClass}`} style={wrapperStyle}>
|
||||
{renderLabel()}
|
||||
<div
|
||||
className="flex h-full w-full items-center justify-center rounded-md border bg-background px-3 text-sm text-muted-foreground"
|
||||
style={heightStyle}
|
||||
>
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
로딩 중...
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -110,8 +182,14 @@ export const CategorySelectComponent: React.FC<
|
||||
// 에러
|
||||
if (error) {
|
||||
return (
|
||||
<div className="flex h-10 w-full items-center rounded-md border border-destructive bg-destructive/10 px-3 text-sm text-destructive">
|
||||
⚠️ {error}
|
||||
<div id={id} className={`relative ${designModeClass}`} style={wrapperStyle}>
|
||||
{renderLabel()}
|
||||
<div
|
||||
className="flex h-full w-full items-center rounded-md border border-destructive bg-destructive/10 px-3 text-sm text-destructive"
|
||||
style={heightStyle}
|
||||
>
|
||||
⚠️ {error}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -119,33 +197,44 @@ export const CategorySelectComponent: React.FC<
|
||||
// 카테고리 값이 없음
|
||||
if (categoryValues.length === 0) {
|
||||
return (
|
||||
<div className="flex h-10 w-full items-center rounded-md border bg-muted px-3 text-sm text-muted-foreground">
|
||||
설정된 카테고리 값이 없습니다
|
||||
<div id={id} className={`relative ${designModeClass}`} style={wrapperStyle}>
|
||||
{renderLabel()}
|
||||
<div
|
||||
className="flex h-full w-full items-center rounded-md border bg-muted px-3 text-sm text-muted-foreground"
|
||||
style={heightStyle}
|
||||
>
|
||||
설정된 카테고리 값이 없습니다
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Select
|
||||
value={value}
|
||||
onValueChange={handleValueChange}
|
||||
disabled={disabled || readonly}
|
||||
required={required}
|
||||
>
|
||||
<SelectTrigger className={`w-full ${className}`}>
|
||||
<SelectValue placeholder={placeholder} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{categoryValues.map((categoryValue) => (
|
||||
<SelectItem
|
||||
key={categoryValue.valueId}
|
||||
value={categoryValue.valueCode}
|
||||
>
|
||||
{categoryValue.valueLabel}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div id={id} className={`relative ${designModeClass}`} style={wrapperStyle}>
|
||||
{renderLabel()}
|
||||
<div className="h-full w-full">
|
||||
<Select
|
||||
value={value}
|
||||
onValueChange={handleValueChange}
|
||||
disabled={disabled || readonly}
|
||||
required={required}
|
||||
>
|
||||
<SelectTrigger className={`w-full h-full ${className}`} style={heightStyle}>
|
||||
<SelectValue placeholder={placeholder} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{categoryValues.map((categoryValue) => (
|
||||
<SelectItem
|
||||
key={categoryValue.valueId}
|
||||
value={categoryValue.valueCode}
|
||||
>
|
||||
{categoryValue.valueLabel}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user