리사이징 유지와 연속작성 구현 모달 살짝 늘어나는 문제 해결

This commit is contained in:
leeheejin 2025-11-24 15:52:45 +09:00
parent 55204dd38c
commit f286b6c695
4 changed files with 275 additions and 144 deletions

View File

@ -57,16 +57,18 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
// 폼 데이터 상태 추가 // 폼 데이터 상태 추가
const [formData, setFormData] = useState<Record<string, any>>({}); const [formData, setFormData] = useState<Record<string, any>>({});
// 연속 등록 모드 상태 (localStorage에 저장하여 리렌더링에 영향받지 않도록) // 연속 등록 모드 상태 (state로 변경 - 체크박스 UI 업데이트를 위해)
const continuousModeRef = useRef(false); const [continuousMode, setContinuousMode] = useState(false);
const [, setForceUpdate] = useState(0); // 강제 리렌더링용 (값은 사용하지 않음)
// 화면 리셋 키 (컴포넌트 강제 리마운트용)
const [resetKey, setResetKey] = useState(0);
// localStorage에서 연속 모드 상태 복원 // localStorage에서 연속 모드 상태 복원
useEffect(() => { useEffect(() => {
const savedMode = localStorage.getItem("screenModal_continuousMode"); const savedMode = localStorage.getItem("screenModal_continuousMode");
if (savedMode === "true") { if (savedMode === "true") {
continuousModeRef.current = true; setContinuousMode(true);
// console.log("🔄 연속 모드 복원: true"); console.log("🔄 연속 모드 복원: true");
} }
}, []); }, []);
@ -162,29 +164,39 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
}); });
setScreenData(null); setScreenData(null);
setFormData({}); setFormData({});
continuousModeRef.current = false; setContinuousMode(false);
localStorage.setItem("screenModal_continuousMode", "false"); // localStorage에 저장 localStorage.setItem("screenModal_continuousMode", "false"); // localStorage에 저장
// console.log("🔄 연속 모드 초기화: false"); console.log("🔄 연속 모드 초기화: false");
}; };
// 저장 성공 이벤트 처리 (연속 등록 모드 지원) // 저장 성공 이벤트 처리 (연속 등록 모드 지원)
const handleSaveSuccess = () => { const handleSaveSuccess = () => {
const isContinuousMode = continuousModeRef.current; const isContinuousMode = continuousMode;
// console.log("💾 저장 성공 이벤트 수신"); console.log("💾 저장 성공 이벤트 수신");
// console.log("📌 현재 연속 모드 상태 (ref):", isContinuousMode); console.log("📌 현재 연속 모드 상태:", isContinuousMode);
// console.log("📌 localStorage:", localStorage.getItem("screenModal_continuousMode")); console.log("📌 localStorage:", localStorage.getItem("screenModal_continuousMode"));
if (isContinuousMode) { if (isContinuousMode) {
// 연속 모드: 폼만 초기화하고 모달은 유지 // 연속 모드: 폼만 초기화하고 모달은 유지
// console.log("✅ 연속 모드 활성화 - 폼만 초기화"); console.log("✅ 연속 모드 활성화 - 폼 초기화 및 화면 리셋");
// 폼만 초기화 (연속 모드 상태는 localStorage에 저장되어 있으므로 유지됨) // 1. 폼 데이터 초기화
setFormData({}); setFormData({});
// 2. 리셋 키 변경 (컴포넌트 강제 리마운트)
setResetKey(prev => prev + 1);
console.log("🔄 resetKey 증가 - 컴포넌트 리마운트");
// 3. 화면 데이터 다시 로드 (채번 규칙 새로 생성)
if (modalState.screenId) {
console.log("🔄 화면 데이터 다시 로드:", modalState.screenId);
loadScreenData(modalState.screenId);
}
toast.success("저장되었습니다. 계속 입력하세요."); toast.success("저장되었습니다. 계속 입력하세요.");
} else { } else {
// 일반 모드: 모달 닫기 // 일반 모드: 모달 닫기
// console.log("❌ 일반 모드 - 모달 닫기"); console.log("❌ 일반 모드 - 모달 닫기");
handleCloseModal(); handleCloseModal();
} }
}; };
@ -198,7 +210,7 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
window.removeEventListener("closeSaveModal", handleCloseModal); window.removeEventListener("closeSaveModal", handleCloseModal);
window.removeEventListener("saveSuccessInModal", handleSaveSuccess); window.removeEventListener("saveSuccessInModal", handleSaveSuccess);
}; };
}, []); // 의존성 제거 (ref 사용으로 최신 상태 참조) }, [continuousMode]); // continuousMode 의존성 추가
// 화면 데이터 로딩 // 화면 데이터 로딩
useEffect(() => { useEffect(() => {
@ -415,18 +427,21 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
setFormData({}); // 폼 데이터 초기화 setFormData({}); // 폼 데이터 초기화
}; };
// 모달 크기 설정 - 화면 내용에 맞게 동적 조정 // 모달 크기 설정 - 화면관리 설정 크기 + 헤더/푸터
const getModalStyle = () => { const getModalStyle = () => {
if (!screenDimensions) { if (!screenDimensions) {
return { return {
className: "w-fit min-w-[400px] max-w-4xl max-h-[90vh] overflow-hidden p-0", className: "w-fit min-w-[400px] max-w-4xl max-h-[90vh] overflow-hidden p-0",
style: {}, style: undefined, // undefined로 변경 - defaultWidth/defaultHeight 사용
}; };
} }
// 헤더 높이를 최소화 (제목 영역만) // 화면관리에서 설정한 크기 = 컨텐츠 영역 크기
const headerHeight = 60; // DialogHeader 최소 높이 (타이틀 + 최소 패딩) // 실제 모달 크기 = 컨텐츠 + 헤더 + 연속등록 체크박스
const totalHeight = screenDimensions.height + headerHeight; const headerHeight = 60; // DialogHeader (타이틀 + 패딩)
const footerHeight = 52; // 연속 등록 모드 체크박스 영역
const totalHeight = screenDimensions.height + headerHeight + footerHeight;
return { return {
className: "overflow-hidden p-0", className: "overflow-hidden p-0",
@ -504,7 +519,7 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
<ResizableDialog open={modalState.isOpen} onOpenChange={handleClose}> <ResizableDialog open={modalState.isOpen} onOpenChange={handleClose}>
<ResizableDialogContent <ResizableDialogContent
className={`${modalStyle.className} ${className || ""}`} className={`${modalStyle.className} ${className || ""}`}
style={modalStyle.style} {...(modalStyle.style && { style: modalStyle.style })} // undefined일 때는 prop 자체를 전달하지 않음
defaultWidth={600} defaultWidth={600}
defaultHeight={800} defaultHeight={800}
minWidth={500} minWidth={500}
@ -530,7 +545,7 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
</div> </div>
</ResizableDialogHeader> </ResizableDialogHeader>
<div className="flex-1 overflow-auto p-6"> <div className="flex-1 overflow-auto">
{loading ? ( {loading ? (
<div className="flex h-full items-center justify-center"> <div className="flex h-full items-center justify-center">
<div className="text-center"> <div className="text-center">
@ -568,7 +583,7 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
return ( return (
<InteractiveScreenViewerDynamic <InteractiveScreenViewerDynamic
key={component.id} key={`${component.id}-${resetKey}`}
component={adjustedComponent} component={adjustedComponent}
allComponents={screenData.components} allComponents={screenData.components}
formData={formData} formData={formData}
@ -607,13 +622,12 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Checkbox <Checkbox
id="continuous-mode" id="continuous-mode"
checked={continuousModeRef.current} checked={continuousMode}
onCheckedChange={(checked) => { onCheckedChange={(checked) => {
const isChecked = checked === true; const isChecked = checked === true;
continuousModeRef.current = isChecked; setContinuousMode(isChecked);
localStorage.setItem("screenModal_continuousMode", String(isChecked)); localStorage.setItem("screenModal_continuousMode", String(isChecked));
setForceUpdate((prev) => prev + 1); // 체크박스 UI 업데이트를 위한 강제 리렌더링 console.log("🔄 연속 모드 변경:", isChecked);
// console.log("🔄 연속 모드 변경:", isChecked);
}} }}
/> />
<Label htmlFor="continuous-mode" className="cursor-pointer text-sm font-normal select-none"> <Label htmlFor="continuous-mode" className="cursor-pointer text-sm font-normal select-none">

View File

@ -92,18 +92,18 @@ export const EditModal: React.FC<EditModalProps> = ({ className }) => {
const contentWidth = maxX - minX; const contentWidth = maxX - minX;
const contentHeight = maxY - minY; const contentHeight = maxY - minY;
// 적절한 여백 추가 // 적절한 여백 추가 (주석처리 - 사용자 설정 크기 그대로 사용)
const paddingX = 40; // const paddingX = 40;
const paddingY = 40; // const paddingY = 40;
const finalWidth = Math.max(contentWidth + paddingX, 400); const finalWidth = Math.max(contentWidth, 400); // padding 제거
const finalHeight = Math.max(contentHeight + paddingY, 300); const finalHeight = Math.max(contentHeight, 300); // padding 제거
return { return {
width: Math.min(finalWidth, window.innerWidth * 0.95), width: Math.min(finalWidth, window.innerWidth * 0.95),
height: Math.min(finalHeight, window.innerHeight * 0.9), height: Math.min(finalHeight, window.innerHeight * 0.9),
offsetX: Math.max(0, minX - paddingX / 2), offsetX: Math.max(0, minX), // paddingX 제거
offsetY: Math.max(0, minY - paddingY / 2), offsetY: Math.max(0, minY), // paddingY 제거
}; };
}; };
@ -269,16 +269,18 @@ export const EditModal: React.FC<EditModalProps> = ({ className }) => {
} }
}; };
// 모달 크기 설정 - ScreenModal과 동일 // 모달 크기 설정 - 화면관리 설정 크기 + 헤더
const getModalStyle = () => { const getModalStyle = () => {
if (!screenDimensions) { if (!screenDimensions) {
return { return {
className: "w-fit min-w-[400px] max-w-4xl max-h-[90vh] overflow-hidden p-0", className: "w-fit min-w-[400px] max-w-4xl max-h-[90vh] overflow-hidden p-0",
style: {}, style: undefined, // undefined로 변경 - defaultWidth/defaultHeight 사용
}; };
} }
const headerHeight = 60; // 화면관리에서 설정한 크기 = 컨텐츠 영역 크기
// 실제 모달 크기 = 컨텐츠 + 헤더
const headerHeight = 60; // DialogHeader
const totalHeight = screenDimensions.height + headerHeight; const totalHeight = screenDimensions.height + headerHeight;
return { return {

View File

@ -216,10 +216,16 @@ export const SaveModal: React.FC<SaveModalProps> = ({
return y + height; return y + height;
})); }));
const padding = 40; // 컨텐츠 영역 크기 (화면관리 설정 크기)
const contentWidth = Math.max(maxX, 400);
const contentHeight = Math.max(maxY, 300);
// 실제 모달 크기 = 컨텐츠 + 헤더
const headerHeight = 60; // DialogHeader
return { return {
width: Math.max(maxX + padding, 400), width: contentWidth,
height: Math.max(maxY + padding, 300), height: contentHeight + headerHeight, // 헤더 높이 포함
}; };
}; };
@ -229,8 +235,12 @@ export const SaveModal: React.FC<SaveModalProps> = ({
<ResizableDialog open={isOpen} onOpenChange={(open) => !isSaving && !open && onClose()}> <ResizableDialog open={isOpen} onOpenChange={(open) => !isSaving && !open && onClose()}>
<ResizableDialogContent <ResizableDialogContent
modalId={`save-modal-${screenId}`} modalId={`save-modal-${screenId}`}
defaultWidth={dynamicSize.width + 48} style={{
defaultHeight={dynamicSize.height + 120} width: `${dynamicSize.width}px`,
height: `${dynamicSize.height}px`, // 화면관리 설정 크기 그대로 사용
}}
defaultWidth={600} // 폴백용 기본값
defaultHeight={400} // 폴백용 기본값
minWidth={400} minWidth={400}
minHeight={300} minHeight={300}
className="gap-0 p-0" className="gap-0 p-0"

View File

@ -122,6 +122,10 @@ const ResizableDialogContent = React.forwardRef<
// 1순위: userStyle에서 크기 추출 (화면관리에서 지정한 크기 - 항상 초기값으로 사용) // 1순위: userStyle에서 크기 추출 (화면관리에서 지정한 크기 - 항상 초기값으로 사용)
if (userStyle) { if (userStyle) {
console.log("🔍 userStyle 감지:", userStyle);
console.log("🔍 userStyle.width 타입:", typeof userStyle.width, "값:", userStyle.width);
console.log("🔍 userStyle.height 타입:", typeof userStyle.height, "값:", userStyle.height);
const styleWidth = typeof userStyle.width === 'string' const styleWidth = typeof userStyle.width === 'string'
? parseInt(userStyle.width) ? parseInt(userStyle.width)
: userStyle.width; : userStyle.width;
@ -129,24 +133,41 @@ const ResizableDialogContent = React.forwardRef<
? parseInt(userStyle.height) ? parseInt(userStyle.height)
: userStyle.height; : userStyle.height;
console.log("📏 파싱된 크기:", {
styleWidth,
styleHeight,
"styleWidth truthy?": !!styleWidth,
"styleHeight truthy?": !!styleHeight,
minWidth,
maxWidth,
minHeight,
maxHeight
});
if (styleWidth && styleHeight) { if (styleWidth && styleHeight) {
return { const finalSize = {
width: Math.max(minWidth, Math.min(maxWidth, styleWidth)), width: Math.max(minWidth, Math.min(maxWidth, styleWidth)),
height: Math.max(minHeight, Math.min(maxHeight, styleHeight)), height: Math.max(minHeight, Math.min(maxHeight, styleHeight)),
}; };
console.log("✅ userStyle 크기 사용:", finalSize);
return finalSize;
} else {
console.log("❌ styleWidth 또는 styleHeight가 falsy:", { styleWidth, styleHeight });
} }
} }
// 2순위: 현재 렌더링된 크기 사용 console.log("⚠️ userStyle 없음, defaultWidth/defaultHeight 사용:", { defaultWidth, defaultHeight });
if (contentRef.current) {
const rect = contentRef.current.getBoundingClientRect(); // 2순위: 현재 렌더링된 크기 사용 (주석처리 - 모달이 열린 후 늘어나는 현상 방지)
if (rect.width > 0 && rect.height > 0) { // if (contentRef.current) {
return { // const rect = contentRef.current.getBoundingClientRect();
width: Math.max(minWidth, Math.min(maxWidth, rect.width)), // if (rect.width > 0 && rect.height > 0) {
height: Math.max(minHeight, Math.min(maxHeight, rect.height)), // return {
}; // width: Math.max(minWidth, Math.min(maxWidth, rect.width)),
} // height: Math.max(minHeight, Math.min(maxHeight, rect.height)),
} // };
// }
// }
// 3순위: defaultWidth/defaultHeight 사용 // 3순위: defaultWidth/defaultHeight 사용
return { width: defaultWidth, height: defaultHeight }; return { width: defaultWidth, height: defaultHeight };
@ -156,6 +177,58 @@ const ResizableDialogContent = React.forwardRef<
const [isResizing, setIsResizing] = React.useState(false); const [isResizing, setIsResizing] = React.useState(false);
const [resizeDirection, setResizeDirection] = React.useState<string>(""); const [resizeDirection, setResizeDirection] = React.useState<string>("");
const [isInitialized, setIsInitialized] = React.useState(false); const [isInitialized, setIsInitialized] = React.useState(false);
// userStyle이 변경되면 크기 업데이트 (화면 데이터 로딩 완료 시)
React.useEffect(() => {
// 1. localStorage에서 사용자가 리사이징한 크기 확인
let savedSize: { width: number; height: number; userResized: boolean } | null = null;
if (effectiveModalId && typeof window !== 'undefined') {
try {
const storageKey = `modal_size_${effectiveModalId}_${userId}`;
const saved = localStorage.getItem(storageKey);
if (saved) {
const parsed = JSON.parse(saved);
if (parsed.userResized) {
savedSize = {
width: Math.max(minWidth, Math.min(maxWidth, parsed.width)),
height: Math.max(minHeight, Math.min(maxHeight, parsed.height)),
userResized: true,
};
console.log("💾 사용자가 리사이징한 크기 복원:", savedSize);
}
}
} catch (error) {
console.error("❌ 모달 크기 복원 실패:", error);
}
}
// 2. 우선순위: 사용자 리사이징 > userStyle > 기본값
if (savedSize && savedSize.userResized) {
// 사용자가 리사이징한 크기 우선
setSize({ width: savedSize.width, height: savedSize.height });
setUserResized(true);
console.log("✅ 사용자 리사이징 크기 적용:", savedSize);
} else if (userStyle && userStyle.width && userStyle.height) {
// 화면관리에서 설정한 크기
const styleWidth = typeof userStyle.width === 'string'
? parseInt(userStyle.width)
: userStyle.width;
const styleHeight = typeof userStyle.height === 'string'
? parseInt(userStyle.height)
: userStyle.height;
if (styleWidth && styleHeight) {
const newSize = {
width: Math.max(minWidth, Math.min(maxWidth, styleWidth)),
height: Math.max(minHeight, Math.min(maxHeight, styleHeight)),
};
console.log("🔄 userStyle 크기 적용:", newSize);
setSize(newSize);
}
}
}, [userStyle, minWidth, maxWidth, minHeight, maxHeight, effectiveModalId, userId]);
const [lastModalId, setLastModalId] = React.useState<string | null>(null); const [lastModalId, setLastModalId] = React.useState<string | null>(null);
const [userResized, setUserResized] = React.useState(false); // 사용자가 실제로 리사이징했는지 추적 const [userResized, setUserResized] = React.useState(false); // 사용자가 실제로 리사이징했는지 추적
@ -192,97 +265,98 @@ const ResizableDialogContent = React.forwardRef<
}, [effectiveModalId, lastModalId, isInitialized]); }, [effectiveModalId, lastModalId, isInitialized]);
// 모달이 열릴 때 초기 크기 설정 (localStorage와 내용 크기 중 큰 값 사용) // 모달이 열릴 때 초기 크기 설정 (localStorage와 내용 크기 중 큰 값 사용)
React.useEffect(() => { // 주석처리 - 사용자가 설정한 크기(userStyle)만 사용하도록 변경
// console.log("🔍 초기 크기 설정 useEffect 실행:", { isInitialized, hasContentRef: !!contentRef.current, effectiveModalId }); // React.useEffect(() => {
// // console.log("🔍 초기 크기 설정 useEffect 실행:", { isInitialized, hasContentRef: !!contentRef.current, effectiveModalId });
if (!isInitialized) { //
// 내용의 실제 크기 측정 (약간의 지연 후, contentRef가 준비될 때까지 대기) // if (!isInitialized) {
// 여러 번 시도하여 contentRef가 준비될 때까지 대기 // // 내용의 실제 크기 측정 (약간의 지연 후, contentRef가 준비될 때까지 대기)
let attempts = 0; // // 여러 번 시도하여 contentRef가 준비될 때까지 대기
const maxAttempts = 10; // let attempts = 0;
// const maxAttempts = 10;
const measureContent = () => { //
attempts++; // const measureContent = () => {
// attempts++;
// scrollHeight/scrollWidth를 사용하여 실제 내용 크기 측정 (스크롤 포함) //
let contentWidth = defaultWidth; // // scrollHeight/scrollWidth를 사용하여 실제 내용 크기 측정 (스크롤 포함)
let contentHeight = defaultHeight; // let contentWidth = defaultWidth;
// let contentHeight = defaultHeight;
if (contentRef.current) { //
// scrollHeight/scrollWidth 그대로 사용 (여유 공간 제거) // // if (contentRef.current) {
contentWidth = contentRef.current.scrollWidth || defaultWidth; // // // scrollHeight/scrollWidth 그대로 사용 (여유 공간 제거)
contentHeight = contentRef.current.scrollHeight || defaultHeight; // // contentWidth = contentRef.current.scrollWidth || defaultWidth;
// // contentHeight = contentRef.current.scrollHeight || defaultHeight;
// console.log("📏 모달 내용 크기 측정:", { attempt: attempts, scrollWidth: contentRef.current.scrollWidth, scrollHeight: contentRef.current.scrollHeight, clientWidth: contentRef.current.clientWidth, clientHeight: contentRef.current.clientHeight, contentWidth, contentHeight }); // //
} else { // // // console.log("📏 모달 내용 크기 측정:", { attempt: attempts, scrollWidth: contentRef.current.scrollWidth, scrollHeight: contentRef.current.scrollHeight, clientWidth: contentRef.current.clientWidth, clientHeight: contentRef.current.clientHeight, contentWidth, contentHeight });
// console.log("⚠️ contentRef 없음, 재시도:", { attempt: attempts, maxAttempts, defaultWidth, defaultHeight }); // // } else {
// // // console.log("⚠️ contentRef 없음, 재시도:", { attempt: attempts, maxAttempts, defaultWidth, defaultHeight });
// contentRef가 아직 없으면 재시도 // //
if (attempts < maxAttempts) { // // // contentRef가 아직 없으면 재시도
setTimeout(measureContent, 100); // // if (attempts < maxAttempts) {
return; // // setTimeout(measureContent, 100);
} // // return;
} // // }
// // }
// 패딩 추가 (p-6 * 2 = 48px) //
const paddingAndMargin = 48; // // 패딩 추가 (p-6 * 2 = 48px)
const initialSize = getInitialSize(); // const paddingAndMargin = 48;
// const initialSize = getInitialSize();
// 내용 크기 기반 최소 크기 계산 //
const contentBasedSize = { // // 내용 크기 기반 최소 크기 계산
width: Math.max(minWidth, Math.min(maxWidth, Math.max(contentWidth + paddingAndMargin, initialSize.width))), // const contentBasedSize = {
height: Math.max(minHeight, Math.min(maxHeight, Math.max(contentHeight + paddingAndMargin, initialSize.height))), // width: Math.max(minWidth, Math.min(maxWidth, Math.max(contentWidth + paddingAndMargin, initialSize.width))),
}; // height: Math.max(minHeight, Math.min(maxHeight, Math.max(contentHeight + paddingAndMargin, initialSize.height))),
// };
// console.log("📐 내용 기반 크기:", contentBasedSize); //
// // console.log("📐 내용 기반 크기:", contentBasedSize);
// localStorage에서 저장된 크기 확인 //
let finalSize = contentBasedSize; // // localStorage에서 저장된 크기 확인
// let finalSize = contentBasedSize;
if (effectiveModalId && typeof window !== 'undefined') { //
try { // if (effectiveModalId && typeof window !== 'undefined') {
const storageKey = `modal_size_${effectiveModalId}_${userId}`; // try {
const saved = localStorage.getItem(storageKey); // const storageKey = `modal_size_${effectiveModalId}_${userId}`;
// const saved = localStorage.getItem(storageKey);
// console.log("📦 localStorage 확인:", { effectiveModalId, userId, storageKey, saved: saved ? "있음" : "없음" }); //
// // console.log("📦 localStorage 확인:", { effectiveModalId, userId, storageKey, saved: saved ? "있음" : "없음" });
if (saved) { //
const parsed = JSON.parse(saved); // if (saved) {
// const parsed = JSON.parse(saved);
// userResized 플래그 확인 //
if (parsed.userResized) { // // userResized 플래그 확인
const savedSize = { // if (parsed.userResized) {
width: Math.max(minWidth, Math.min(maxWidth, parsed.width)), // const savedSize = {
height: Math.max(minHeight, Math.min(maxHeight, parsed.height)), // width: Math.max(minWidth, Math.min(maxWidth, parsed.width)),
}; // height: Math.max(minHeight, Math.min(maxHeight, parsed.height)),
// };
// console.log("💾 사용자가 리사이징한 크기 복원:", savedSize); //
// // console.log("💾 사용자가 리사이징한 크기 복원:", savedSize);
// ✅ 중요: 사용자가 명시적으로 리사이징한 경우, 사용자 크기를 우선 사용 //
// (사용자가 의도적으로 작게 만든 것을 존중) // // ✅ 중요: 사용자가 명시적으로 리사이징한 경우, 사용자 크기를 우선 사용
finalSize = savedSize; // // (사용자가 의도적으로 작게 만든 것을 존중)
setUserResized(true); // finalSize = savedSize;
// setUserResized(true);
// console.log("✅ 최종 크기 (사용자가 설정한 크기 우선 적용):", { savedSize, contentBasedSize, finalSize, note: "사용자가 리사이징한 크기를 그대로 사용합니다" }); //
} else { // // console.log("✅ 최종 크기 (사용자가 설정한 크기 우선 적용):", { savedSize, contentBasedSize, finalSize, note: "사용자가 리사이징한 크기를 그대로 사용합니다" });
// console.log(" 자동 계산된 크기는 무시, 내용 크기 사용"); // } else {
} // // console.log(" 자동 계산된 크기는 무시, 내용 크기 사용");
} else { // }
// console.log(" localStorage에 저장된 크기 없음, 내용 크기 사용"); // } else {
} // // console.log(" localStorage에 저장된 크기 없음, 내용 크기 사용");
} catch (error) { // }
// console.error("❌ 모달 크기 복원 실패:", error); // } catch (error) {
} // // console.error("❌ 모달 크기 복원 실패:", error);
} // }
// }
setSize(finalSize); //
setIsInitialized(true); // setSize(finalSize);
}; // setIsInitialized(true);
// };
// 첫 시도는 300ms 후에 시작 //
setTimeout(measureContent, 300); // // 첫 시도는 300ms 후에 시작
} // setTimeout(measureContent, 300);
}, [isInitialized, getInitialSize, effectiveModalId, userId, minWidth, maxWidth, minHeight, maxHeight, defaultWidth, defaultHeight]); // }
// }, [isInitialized, getInitialSize, effectiveModalId, userId, minWidth, maxWidth, minHeight, maxHeight, defaultWidth, defaultHeight]);
const startResize = (direction: string) => (e: React.MouseEvent) => { const startResize = (direction: string) => (e: React.MouseEvent) => {
e.preventDefault(); e.preventDefault();
@ -433,6 +507,37 @@ const ResizableDialogContent = React.forwardRef<
onMouseDown={startResize("nw")} onMouseDown={startResize("nw")}
/> />
{/* 리셋 버튼 (사용자가 리사이징한 경우만 표시) */}
{userResized && (
<button
onClick={() => {
// localStorage에서 저장된 크기 삭제
if (effectiveModalId && typeof window !== 'undefined') {
const storageKey = `modal_size_${effectiveModalId}_${userId}`;
localStorage.removeItem(storageKey);
console.log("🗑️ 저장된 모달 크기 삭제:", storageKey);
}
// 화면관리 설정 크기로 복원
const initialSize = getInitialSize();
setSize(initialSize);
setUserResized(false);
console.log("🔄 기본 크기로 리셋:", initialSize);
}}
className="absolute right-12 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
style={{ zIndex: 20 }}
title="기본 크기로 리셋"
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/>
<path d="M21 3v5h-5"/>
<path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/>
<path d="M3 21v-5h5"/>
</svg>
<span className="sr-only"> </span>
</button>
)}
<DialogPrimitive.Close <DialogPrimitive.Close
className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground" className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
style={{ zIndex: 20 }} style={{ zIndex: 20 }}