분할패널 버튼 이동 가능하게 수정

This commit is contained in:
kjs
2025-12-11 18:40:39 +09:00
parent 88024b4e60
commit 016b8f707b
8 changed files with 1021 additions and 162 deletions

View File

@@ -50,6 +50,7 @@ import { cn } from "@/lib/utils";
import { useScreenPreview } from "@/contexts/ScreenPreviewContext";
import { TableOptionsProvider } from "@/contexts/TableOptionsContext";
import { TableOptionsToolbar } from "./table-options/TableOptionsToolbar";
import { SplitPanelProvider } from "@/lib/registry/components/split-panel-layout/SplitPanelContext";
/**
* 🔗 연쇄 드롭다운 래퍼 컴포넌트
@@ -2101,113 +2102,115 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
: component;
return (
<TableOptionsProvider>
<div className="flex h-full flex-col">
{/* 테이블 옵션 툴바 */}
<TableOptionsToolbar />
{/* 메인 컨텐츠 */}
<div className="h-full flex-1" style={{ width: '100%' }}>
{/* 라벨이 있는 경우 표시 (데이터 테이블 제외) */}
{shouldShowLabel && (
<label className="mb-2 block text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
{labelText}
{(component.required || component.componentConfig?.required) && <span className="ml-1 text-destructive">*</span>}
</label>
)}
{/* 실제 위젯 - 상위에서 라벨을 렌더링했으므로 자식은 라벨 숨김 */}
<div className="h-full" style={{ width: '100%', height: '100%' }}>{renderInteractiveWidget(componentForRendering)}</div>
</div>
</div>
{/* 개선된 검증 패널 (선택적 표시) */}
{showValidationPanel && enhancedValidation && (
<div className="absolute bottom-4 right-4 z-50">
<FormValidationIndicator
validationState={enhancedValidation.validationState}
saveState={enhancedValidation.saveState}
onSave={async () => {
const success = await enhancedValidation.saveForm();
if (success) {
toast.success("데이터가 성공적으로 저장되었습니다!");
}
}}
canSave={enhancedValidation.canSave}
compact={true}
showDetails={false}
/>
</div>
)}
{/* 모달 화면 */}
<Dialog open={!!popupScreen} onOpenChange={() => {
setPopupScreen(null);
setPopupFormData({}); // 팝업 닫을 때 formData도 초기화
}}>
<DialogContent className="max-w-4xl max-h-[90vh] overflow-hidden p-0">
<DialogHeader className="px-6 pt-4 pb-2">
<DialogTitle>{popupScreen?.title || "상세 정보"}</DialogTitle>
</DialogHeader>
<SplitPanelProvider>
<TableOptionsProvider>
<div className="flex h-full flex-col">
{/* 테이블 옵션 툴바 */}
<TableOptionsToolbar />
<div className="overflow-y-auto px-6 pb-6" style={{ maxHeight: "calc(90vh - 80px)" }}>
{popupLoading ? (
<div className="flex items-center justify-center py-8">
<div className="text-muted-foreground"> ...</div>
</div>
) : popupLayout.length > 0 ? (
<div className="relative bg-background border rounded" style={{
width: popupScreenResolution ? `${popupScreenResolution.width}px` : "100%",
height: popupScreenResolution ? `${popupScreenResolution.height}px` : "400px",
minHeight: "400px",
position: "relative",
overflow: "hidden"
}}>
{/* 팝업에서도 실제 위치와 크기로 렌더링 */}
{popupLayout.map((popupComponent) => (
<div
key={popupComponent.id}
className="absolute"
style={{
left: `${popupComponent.position.x}px`,
top: `${popupComponent.position.y}px`,
width: popupComponent.style?.width || `${popupComponent.size.width}px`,
height: popupComponent.style?.height || `${popupComponent.size.height}px`,
zIndex: Math.min(popupComponent.position.z || 1, 20), // 최대 z-index 20으로 제한
}}
>
{/* 🎯 핵심 수정: 팝업 전용 formData 사용 */}
<InteractiveScreenViewer
component={popupComponent}
allComponents={popupLayout}
hideLabel={false}
screenInfo={popupScreenInfo || undefined}
formData={popupFormData}
onFormDataChange={(fieldName, value) => {
console.log("💾 팝업 formData 업데이트:", {
fieldName,
value,
valueType: typeof value,
prevFormData: popupFormData
});
setPopupFormData(prev => ({
...prev,
[fieldName]: value
}));
}}
/>
</div>
))}
</div>
) : (
<div className="flex items-center justify-center py-8">
<div className="text-muted-foreground"> .</div>
</div>
{/* 메인 컨텐츠 */}
<div className="h-full flex-1" style={{ width: '100%' }}>
{/* 라벨이 있는 경우 표시 (데이터 테이블 제외) */}
{shouldShowLabel && (
<label className="mb-2 block text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
{labelText}
{(component.required || component.componentConfig?.required) && <span className="ml-1 text-destructive">*</span>}
</label>
)}
{/* 실제 위젯 - 상위에서 라벨을 렌더링했으므로 자식은 라벨 숨김 */}
<div className="h-full" style={{ width: '100%', height: '100%' }}>{renderInteractiveWidget(componentForRendering)}</div>
</div>
</DialogContent>
</Dialog>
</TableOptionsProvider>
</div>
{/* 개선된 검증 패널 (선택적 표시) */}
{showValidationPanel && enhancedValidation && (
<div className="absolute bottom-4 right-4 z-50">
<FormValidationIndicator
validationState={enhancedValidation.validationState}
saveState={enhancedValidation.saveState}
onSave={async () => {
const success = await enhancedValidation.saveForm();
if (success) {
toast.success("데이터가 성공적으로 저장되었습니다!");
}
}}
canSave={enhancedValidation.canSave}
compact={true}
showDetails={false}
/>
</div>
)}
{/* 모달 화면 */}
<Dialog open={!!popupScreen} onOpenChange={() => {
setPopupScreen(null);
setPopupFormData({}); // 팝업 닫을 때 formData도 초기화
}}>
<DialogContent className="max-w-4xl max-h-[90vh] overflow-hidden p-0">
<DialogHeader className="px-6 pt-4 pb-2">
<DialogTitle>{popupScreen?.title || "상세 정보"}</DialogTitle>
</DialogHeader>
<div className="overflow-y-auto px-6 pb-6" style={{ maxHeight: "calc(90vh - 80px)" }}>
{popupLoading ? (
<div className="flex items-center justify-center py-8">
<div className="text-muted-foreground"> ...</div>
</div>
) : popupLayout.length > 0 ? (
<div className="relative bg-background border rounded" style={{
width: popupScreenResolution ? `${popupScreenResolution.width}px` : "100%",
height: popupScreenResolution ? `${popupScreenResolution.height}px` : "400px",
minHeight: "400px",
position: "relative",
overflow: "hidden"
}}>
{/* 팝업에서도 실제 위치와 크기로 렌더링 */}
{popupLayout.map((popupComponent) => (
<div
key={popupComponent.id}
className="absolute"
style={{
left: `${popupComponent.position.x}px`,
top: `${popupComponent.position.y}px`,
width: popupComponent.style?.width || `${popupComponent.size.width}px`,
height: popupComponent.style?.height || `${popupComponent.size.height}px`,
zIndex: Math.min(popupComponent.position.z || 1, 20), // 최대 z-index 20으로 제한
}}
>
{/* 🎯 핵심 수정: 팝업 전용 formData 사용 */}
<InteractiveScreenViewer
component={popupComponent}
allComponents={popupLayout}
hideLabel={false}
screenInfo={popupScreenInfo || undefined}
formData={popupFormData}
onFormDataChange={(fieldName, value) => {
console.log("💾 팝업 formData 업데이트:", {
fieldName,
value,
valueType: typeof value,
prevFormData: popupFormData
});
setPopupFormData(prev => ({
...prev,
[fieldName]: value
}));
}}
/>
</div>
))}
</div>
) : (
<div className="flex items-center justify-center py-8">
<div className="text-muted-foreground"> .</div>
</div>
)}
</div>
</DialogContent>
</Dialog>
</TableOptionsProvider>
</SplitPanelProvider>
);
};