화면 분할패널 커밋

This commit is contained in:
kjs
2025-11-27 12:54:57 +09:00
parent 454f79caec
commit 51c49f7a3d
4 changed files with 419 additions and 3 deletions

View File

@@ -23,6 +23,8 @@ import { toast } from "sonner";
import { filterDOMProps } from "@/lib/utils/domPropsFilter";
import { useCurrentFlowStep } from "@/stores/flowStepStore";
import { useScreenPreview } from "@/contexts/ScreenPreviewContext";
import { useScreenContextOptional } from "@/contexts/ScreenContext";
import { applyMappingRules } from "@/lib/utils/dataMapping";
export interface ButtonPrimaryComponentProps extends ComponentRendererProps {
config?: ButtonPrimaryConfig;
@@ -97,6 +99,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
...props
}) => {
const { isPreviewMode } = useScreenPreview(); // 프리뷰 모드 확인
const screenContext = useScreenContextOptional(); // 화면 컨텍스트
// 🆕 props에서 onSave 추출 (명시적으로 선언되지 않은 경우 ...props에서 추출)
const propsOnSave = (props as any).onSave as (() => Promise<void>) | undefined;
@@ -374,6 +377,106 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
};
// 이벤트 핸들러
/**
* transferData 액션 처리
*/
const handleTransferDataAction = async (actionConfig: any) => {
const dataTransferConfig = actionConfig.dataTransfer;
if (!dataTransferConfig) {
toast.error("데이터 전달 설정이 없습니다.");
return;
}
if (!screenContext) {
toast.error("화면 컨텍스트를 찾을 수 없습니다.");
return;
}
try {
// 1. 소스 컴포넌트에서 데이터 가져오기
const sourceProvider = screenContext.getDataProvider(dataTransferConfig.sourceComponentId);
if (!sourceProvider) {
toast.error(`소스 컴포넌트를 찾을 수 없습니다: ${dataTransferConfig.sourceComponentId}`);
return;
}
const sourceData = sourceProvider.getSelectedData();
if (!sourceData || sourceData.length === 0) {
toast.warning("선택된 데이터가 없습니다.");
return;
}
// 2. 검증
const validation = dataTransferConfig.validation;
if (validation) {
if (validation.minSelection && sourceData.length < validation.minSelection) {
toast.error(`최소 ${validation.minSelection}개 이상 선택해야 합니다.`);
return;
}
if (validation.maxSelection && sourceData.length > validation.maxSelection) {
toast.error(`최대 ${validation.maxSelection}개까지 선택할 수 있습니다.`);
return;
}
}
// 3. 확인 메시지
if (dataTransferConfig.confirmBeforeTransfer) {
const confirmMessage = dataTransferConfig.confirmMessage || `${sourceData.length}개 항목을 전달하시겠습니까?`;
if (!window.confirm(confirmMessage)) {
return;
}
}
// 4. 매핑 규칙 적용
const mappedData = sourceData.map((row) => {
return applyMappingRules(row, dataTransferConfig.mappingRules || []);
});
console.log("📦 데이터 전달:", {
sourceData,
mappedData,
targetType: dataTransferConfig.targetType,
targetComponentId: dataTransferConfig.targetComponentId,
targetScreenId: dataTransferConfig.targetScreenId,
});
// 5. 타겟으로 데이터 전달
if (dataTransferConfig.targetType === "component") {
// 같은 화면의 컴포넌트로 전달
const targetReceiver = screenContext.getDataReceiver(dataTransferConfig.targetComponentId);
if (!targetReceiver) {
toast.error(`타겟 컴포넌트를 찾을 수 없습니다: ${dataTransferConfig.targetComponentId}`);
return;
}
await targetReceiver.receiveData(mappedData, {
targetComponentId: dataTransferConfig.targetComponentId,
targetComponentType: targetReceiver.componentType,
mode: dataTransferConfig.mode || "append",
mappingRules: dataTransferConfig.mappingRules || [],
});
} else if (dataTransferConfig.targetType === "screen") {
// 다른 화면으로 전달 (구현 예정)
toast.info("다른 화면으로의 데이터 전달은 추후 구현 예정입니다.");
}
toast.success(`${sourceData.length}개 항목이 전달되었습니다.`);
// 6. 전달 후 정리
if (dataTransferConfig.clearAfterTransfer) {
sourceProvider.clearSelection();
}
} catch (error: any) {
console.error("❌ 데이터 전달 실패:", error);
toast.error(error.message || "데이터 전달 중 오류가 발생했습니다.");
}
};
const handleClick = async (e: React.MouseEvent) => {
e.stopPropagation();
@@ -390,6 +493,12 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
// 인터랙티브 모드에서 액션 실행
if (isInteractive && processedConfig.action) {
// transferData 액션 처리 (화면 컨텍스트 필요)
if (processedConfig.action.type === "transferData") {
await handleTransferDataAction(processedConfig.action);
return;
}
// 삭제 액션인데 선택된 데이터가 없으면 경고 메시지 표시하고 중단
const hasDataToDelete =
(selectedRowsData && selectedRowsData.length > 0) || (flowSelectedData && flowSelectedData.length > 0);