화면 분할 패널 기능

This commit is contained in:
kjs
2025-11-28 14:56:11 +09:00
parent 30dac204c0
commit f15846fd10
27 changed files with 2455 additions and 207 deletions

View File

@@ -12,6 +12,8 @@ import {
import { ConditionalContainerProps, ConditionalSection } from "./types";
import { ConditionalSectionViewer } from "./ConditionalSectionViewer";
import { cn } from "@/lib/utils";
import { useScreenContextOptional } from "@/contexts/ScreenContext";
import type { DataProvidable } from "@/types/data-transfer";
/**
* 조건부 컨테이너 컴포넌트
@@ -42,6 +44,9 @@ export function ConditionalContainerComponent({
onSave, // 🆕 EditModal의 handleSave 콜백
}: ConditionalContainerProps) {
// 화면 컨텍스트 (데이터 제공자로 등록)
const screenContext = useScreenContextOptional();
// config prop 우선, 없으면 개별 prop 사용
const controlField = config?.controlField || propControlField || "condition";
const controlLabel = config?.controlLabel || propControlLabel || "조건 선택";
@@ -50,30 +55,86 @@ export function ConditionalContainerComponent({
const showBorder = config?.showBorder ?? propShowBorder ?? true;
const spacing = config?.spacing || propSpacing || "normal";
// 초기값 계산 (한 번만)
const initialValue = React.useMemo(() => {
return value || formData?.[controlField] || defaultValue || "";
}, []); // 의존성 없음 - 마운트 시 한 번만 계산
// 현재 선택된 값
const [selectedValue, setSelectedValue] = useState<string>(
value || formData?.[controlField] || defaultValue || ""
);
const [selectedValue, setSelectedValue] = useState<string>(initialValue);
// 최신 값을 ref로 유지 (클로저 문제 방지)
const selectedValueRef = React.useRef(selectedValue);
selectedValueRef.current = selectedValue; // 렌더링마다 업데이트 (useEffect 대신)
// formData 변경 시 동기화
useEffect(() => {
if (formData?.[controlField]) {
setSelectedValue(formData[controlField]);
}
}, [formData, controlField]);
// 값 변경 핸들러
const handleValueChange = (newValue: string) => {
// 콜백 refs (의존성 제거)
const onChangeRef = React.useRef(onChange);
const onFormDataChangeRef = React.useRef(onFormDataChange);
onChangeRef.current = onChange;
onFormDataChangeRef.current = onFormDataChange;
// 값 변경 핸들러 - 의존성 없음
const handleValueChange = React.useCallback((newValue: string) => {
// 같은 값이면 무시
if (newValue === selectedValueRef.current) return;
setSelectedValue(newValue);
if (onChange) {
onChange(newValue);
if (onChangeRef.current) {
onChangeRef.current(newValue);
}
if (onFormDataChange) {
onFormDataChange(controlField, newValue);
if (onFormDataChangeRef.current) {
onFormDataChangeRef.current(controlField, newValue);
}
};
}, [controlField]);
// sectionsRef 추가 (dataProvider에서 사용)
const sectionsRef = React.useRef(sections);
React.useEffect(() => {
sectionsRef.current = sections;
}, [sections]);
// dataProvider를 useMemo로 감싸서 불필요한 재생성 방지
const dataProvider = React.useMemo<DataProvidable>(() => ({
componentId: componentId || "conditional-container",
componentType: "conditional-container",
getSelectedData: () => {
// ref를 통해 최신 값 참조 (클로저 문제 방지)
const currentValue = selectedValueRef.current;
const currentSections = sectionsRef.current;
return [{
[controlField]: currentValue,
condition: currentValue,
label: currentSections.find(s => s.condition === currentValue)?.label || currentValue,
}];
},
getAllData: () => {
const currentSections = sectionsRef.current;
return currentSections.map(section => ({
condition: section.condition,
label: section.label,
}));
},
clearSelection: () => {
// 조건부 컨테이너는 초기화하지 않음
console.log("조건부 컨테이너는 선택 초기화를 지원하지 않습니다.");
},
}), [componentId, controlField]); // selectedValue, sections는 ref로 참조
// 화면 컨텍스트에 데이터 제공자로 등록
useEffect(() => {
if (screenContext && componentId) {
screenContext.registerDataProvider(componentId, dataProvider);
return () => {
screenContext.unregisterDataProvider(componentId);
};
}
}, [screenContext, componentId, dataProvider]);
// 컨테이너 높이 측정용 ref
const containerRef = useRef<HTMLDivElement>(null);
@@ -158,6 +219,8 @@ export function ConditionalContainerComponent({
onFormDataChange={onFormDataChange}
groupedData={groupedData}
onSave={onSave}
controlField={controlField}
selectedCondition={selectedValue}
/>
))}
</div>
@@ -179,6 +242,8 @@ export function ConditionalContainerComponent({
onFormDataChange={onFormDataChange}
groupedData={groupedData}
onSave={onSave}
controlField={controlField}
selectedCondition={selectedValue}
/>
) : null
)