feat: Enhance BOM and UI components with improved label handling and data mapping
- Updated the BOM service to include additional fields in the BOM header retrieval, enhancing data richness. - Enhanced the EditModal to automatically map foreign key fields to dot notation, improving data handling and user experience. - Improved the rendering of labels in various components, allowing for customizable label positions and styles, enhancing UI flexibility. - Added new properties for label positioning and spacing in the V2 component styles, allowing for better layout control. - Enhanced the BomTreeComponent to support additional data mapping for entity joins, improving data accessibility and management.
This commit is contained in:
@@ -961,36 +961,83 @@ export const V2Input = forwardRef<HTMLDivElement, V2InputProps>((props, ref) =>
|
||||
}
|
||||
};
|
||||
|
||||
// 라벨이 표시될 때 입력 필드가 차지할 높이 계산
|
||||
// 🔧 label prop이 없어도 style.labelText에서 가져올 수 있도록 수정
|
||||
const actualLabel = label || style?.labelText;
|
||||
const showLabel = actualLabel && style?.labelDisplay === true;
|
||||
// size에서 우선 가져오고, 없으면 style에서 가져옴
|
||||
const componentWidth = size?.width || style?.width;
|
||||
const componentHeight = size?.height || style?.height;
|
||||
|
||||
// 라벨 높이 계산 (기본 20px, 사용자 설정에 따라 조정)
|
||||
// 라벨 위치 및 높이 계산
|
||||
const labelPos = style?.labelPosition || "top";
|
||||
const isHorizLabel = labelPos === "left" || labelPos === "right";
|
||||
const labelFontSize = style?.labelFontSize ? parseInt(String(style.labelFontSize)) : 14;
|
||||
const labelMarginBottom = style?.labelMarginBottom ? parseInt(String(style.labelMarginBottom)) : 4;
|
||||
const estimatedLabelHeight = labelFontSize + labelMarginBottom + 2; // 라벨 높이 + 여백
|
||||
const estimatedLabelHeight = labelFontSize + labelMarginBottom + 2;
|
||||
const labelGapValue = style?.labelGap || "8px";
|
||||
|
||||
// 🔧 커스텀 스타일 감지 (StyleEditor에서 설정한 테두리/배경/텍스트 스타일)
|
||||
// RealtimePreview 래퍼가 외부 div에 스타일을 적용하지만,
|
||||
// 내부 input/textarea가 자체 Tailwind 테두리를 가지므로 이를 제거하여 외부 스타일이 보이도록 함
|
||||
// 커스텀 스타일 감지
|
||||
const hasCustomBorder = !!(style?.borderWidth || style?.borderColor || style?.borderStyle || style?.border);
|
||||
const hasCustomBackground = !!style?.backgroundColor;
|
||||
const hasCustomRadius = !!style?.borderRadius;
|
||||
|
||||
// 텍스트 스타일 오버라이드 (내부 input/textarea에 직접 전달)
|
||||
const customTextStyle: React.CSSProperties = {};
|
||||
if (style?.color) customTextStyle.color = style.color;
|
||||
if (style?.fontSize) customTextStyle.fontSize = style.fontSize;
|
||||
if (style?.fontWeight) customTextStyle.fontWeight = style.fontWeight;
|
||||
if (style?.textAlign) customTextStyle.textAlign = style.textAlign as React.CSSProperties["textAlign"];
|
||||
const hasCustomText = Object.keys(customTextStyle).length > 0;
|
||||
// 내부 input에 직접 적용할 텍스트 스타일 (fontSize, color, fontWeight, textAlign)
|
||||
const inputTextStyle: React.CSSProperties | undefined = hasCustomText ? customTextStyle : undefined;
|
||||
|
||||
const labelElement = showLabel ? (
|
||||
<Label
|
||||
htmlFor={id}
|
||||
style={{
|
||||
...(labelPos === "top" ? { position: "absolute" as const, top: `-${estimatedLabelHeight}px`, left: 0 } : {}),
|
||||
...(labelPos === "bottom" ? { position: "absolute" as const, bottom: `-${estimatedLabelHeight}px`, left: 0 } : {}),
|
||||
fontSize: style?.labelFontSize || "14px",
|
||||
color: style?.labelColor || "#64748b",
|
||||
fontWeight: style?.labelFontWeight || "500",
|
||||
}}
|
||||
className="text-sm font-medium whitespace-nowrap"
|
||||
>
|
||||
{actualLabel}
|
||||
{required && <span className="ml-0.5 text-orange-500">*</span>}
|
||||
</Label>
|
||||
) : null;
|
||||
|
||||
const inputContent = (
|
||||
<div
|
||||
className={cn(
|
||||
isHorizLabel ? "min-w-0 flex-1" : "h-full w-full",
|
||||
hasCustomBorder && "[&_input]:border-0! [&_textarea]:border-0! [&_.border]:border-0!",
|
||||
(hasCustomBorder || hasCustomRadius) && "[&_input]:rounded-none! [&_textarea]:rounded-none! [&_.rounded-md]:rounded-none!",
|
||||
hasCustomBackground && "[&_input]:bg-transparent! [&_textarea]:bg-transparent!",
|
||||
)}
|
||||
style={{ ...(hasCustomText ? customTextStyle : {}), ...(isHorizLabel ? { height: "100%" } : {}) }}
|
||||
>
|
||||
{renderInput()}
|
||||
</div>
|
||||
);
|
||||
|
||||
if (isHorizLabel && showLabel) {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
id={id}
|
||||
style={{
|
||||
width: componentWidth,
|
||||
height: componentHeight,
|
||||
display: "flex",
|
||||
flexDirection: labelPos === "left" ? "row" : "row-reverse",
|
||||
alignItems: "center",
|
||||
gap: labelGapValue,
|
||||
}}
|
||||
>
|
||||
{labelElement}
|
||||
{inputContent}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
@@ -1001,38 +1048,8 @@ export const V2Input = forwardRef<HTMLDivElement, V2InputProps>((props, ref) =>
|
||||
height: componentHeight,
|
||||
}}
|
||||
>
|
||||
{/* 🔧 라벨을 absolute로 컴포넌트 위에 배치 (높이에 포함되지 않음) */}
|
||||
{showLabel && (
|
||||
<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"
|
||||
>
|
||||
{actualLabel}
|
||||
{required && <span className="ml-0.5 text-orange-500">*</span>}
|
||||
</Label>
|
||||
)}
|
||||
<div
|
||||
className={cn(
|
||||
"h-full w-full",
|
||||
// 커스텀 테두리 설정 시, 내부 input/textarea의 기본 테두리 제거 (외부 래퍼 스타일이 보이도록)
|
||||
hasCustomBorder && "[&_input]:border-0! [&_textarea]:border-0! [&_.border]:border-0!",
|
||||
// 커스텀 모서리 설정 시, 내부 요소의 기본 모서리 제거 (외부 래퍼가 처리)
|
||||
(hasCustomBorder || hasCustomRadius) && "[&_input]:rounded-none! [&_textarea]:rounded-none! [&_.rounded-md]:rounded-none!",
|
||||
// 커스텀 배경 설정 시, 내부 input을 투명하게 (외부 배경이 보이도록)
|
||||
hasCustomBackground && "[&_input]:bg-transparent! [&_textarea]:bg-transparent!",
|
||||
)}
|
||||
style={hasCustomText ? customTextStyle : undefined}
|
||||
>
|
||||
{renderInput()}
|
||||
</div>
|
||||
{labelElement}
|
||||
{inputContent}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user