diff --git a/src/hooks/roofcover/useOuterLineWall.js b/src/hooks/roofcover/useOuterLineWall.js
index 0c49fccb..82472227 100644
--- a/src/hooks/roofcover/useOuterLineWall.js
+++ b/src/hooks/roofcover/useOuterLineWall.js
@@ -6,9 +6,11 @@ import {
adsorptionPointModeState,
adsorptionRangeState,
canvasState,
+ currentMenuState,
dotLineIntervalSelector,
verticalHorizontalModeState,
} from '@/store/canvasAtom'
+import { MENU } from '@/common/common'
import { useEvent } from '@/hooks/useEvent'
import { useMouse } from '@/hooks/useMouse'
import { useLine } from '@/hooks/useLine'
@@ -88,6 +90,7 @@ export function useOuterLineWall(id, propertiesId) {
const { addPopup, closePopup } = usePopup()
const setOuterLineFix = useSetRecoilState(outerLineFixState)
+ const setCurrentMenu = useSetRecoilState(currentMenuState)
const outerLineDiagonalLengthRef = useRef(null)
@@ -257,6 +260,8 @@ export function useOuterLineWall(id, propertiesId) {
canvas.outerLineFix = true
closePopup(id)
ccwCheck()
+ // 외벽선 완료 → 지붕형상 설정 메뉴로 전환
+ setCurrentMenu(MENU.ROOF_COVERING.ROOF_SHAPE_SETTINGS)
addPopup(propertiesId, 1, )
}
diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js
index e7c8e260..9a1a18c6 100644
--- a/src/hooks/roofcover/useRoofShapeSetting.js
+++ b/src/hooks/roofcover/useRoofShapeSetting.js
@@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react'
import { useMessage } from '@/hooks/useMessage'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, currentMenuState, currentObjectState, pitchTextSelector } from '@/store/canvasAtom'
-import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
+import { LINE_TYPE, MENU, POLYGON_TYPE } from '@/common/common'
import { usePolygon } from '@/hooks/usePolygon'
import { useMode } from '@/hooks/useMode'
import { useLine } from '@/hooks/useLine'
@@ -21,6 +21,8 @@ import {
import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom'
import { useUndoRedo } from '@/hooks/useUndoRedo'
+import { pendingPopupState } from '@/store/popupAtom'
+import RoofShapeSetting from '@/components/floor-plan/modal/roofShape/RoofShapeSetting'
// 지붕형상 설정
export function useRoofShapeSetting(id) {
@@ -71,6 +73,7 @@ export function useRoofShapeSetting(id) {
const { addLine, removeLine } = useLine()
const { saveSnapshot } = useUndoRedo()
+ const [pendingPopup, setPendingPopup] = useRecoilState(pendingPopupState)
useEffect(() => {
pitchRef.current = currentAngleType === ANGLE_TYPE.SLOPE ? pitch : getChonByDegree(pitch)
@@ -82,6 +85,18 @@ export function useRoofShapeSetting(id) {
jerkinHeadPitchRef.current = currentAngleType === ANGLE_TYPE.SLOPE ? jerkinHeadPitch : getChonByDegree(jerkinHeadPitch)
}, [jerkinHeadPitch])
+ // undo/redo 로 pendingPopup 이 복원되면 해당 팝업을 다시 연다
+ useEffect(() => {
+ if (!pendingPopup) return
+ if (pendingPopup.type === 'RoofShapeSetting') {
+ addPopup(pendingPopup.id, 1, )
+ setPendingPopup(null)
+ } else if (pendingPopup.type === 'RoofAllocation') {
+ addPopup(pendingPopup.id, 1, )
+ setPendingPopup(null)
+ }
+ }, [pendingPopup])
+
useEffect(() => {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
if (!canvas.outerLineFix || outerLines.length === 0) {
@@ -184,7 +199,8 @@ export function useRoofShapeSetting(id) {
]
const handleSave = () => {
- saveSnapshot()
+ // undo 시 지붕형상 설정 팝업이 다시 열리도록 pendingPopup 을 atomOverrides 로 직접 전달
+ saveSnapshot({ pendingPopup: { type: 'RoofShapeSetting', id, pos: { x: 50, y: 230 } } })
let outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
let direction
@@ -485,6 +501,8 @@ export function useRoofShapeSetting(id) {
canvas?.renderAll()
roof.drawHelpLine(settingModalFirstOptions)
isFixRef.current = true
+ // 지붕형상 설정 완료 → 지붕면 할당 메뉴로 전환
+ setCurrentMenu(MENU.ROOF_COVERING.ROOF_SHAPE_ALLOC)
addPopup(id, 1, )
}
diff --git a/src/hooks/useUndoRedo.js b/src/hooks/useUndoRedo.js
index 1a93ff5e..21bfc500 100644
--- a/src/hooks/useUndoRedo.js
+++ b/src/hooks/useUndoRedo.js
@@ -15,7 +15,7 @@ import {
} from '@/store/canvasAtom'
import { outerLinePointsState } from '@/store/outerLineAtom'
import { selectedMenuState } from '@/store/menuAtom'
-import { popupState } from '@/store/popupAtom'
+import { pendingPopupState, popupState } from '@/store/popupAtom'
import { POLYGON_TYPE, SAVE_KEY } from '@/common/common'
const MAX_HISTORY = 3
@@ -103,6 +103,7 @@ export function useUndoRedo() {
const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState)
const [, setCanvasZoom] = useRecoilState(canvasZoomState)
const setPopup = useSetRecoilState(popupState)
+ const [pendingPopup, setPendingPopup] = useRecoilState(pendingPopupState)
// 최신 스택 값을 ref로 유지하여 클로저 stale 문제 방지
const undoStackRef = useRef(undoStack)
@@ -118,8 +119,9 @@ export function useUndoRedo() {
outerLinePoints: safeClone(outerLinePoints || []),
selectedMenu,
currentMenu,
+ pendingPopup: pendingPopup ? safeClone(pendingPopup) : null,
}
- }, [moduleIsSetup, checkedModule, moduleRowColArray, outerLinePoints, selectedMenu, currentMenu])
+ }, [moduleIsSetup, checkedModule, moduleRowColArray, outerLinePoints, selectedMenu, currentMenu, pendingPopup])
// loadFromJSON 후 오브젝트 간 라이브 참조 관계를 재구축
const rebuildObjectGraph = useCallback(() => {
@@ -266,6 +268,8 @@ export function useUndoRedo() {
setOuterLinePoints(atomSnapshot.outerLinePoints || [])
if (atomSnapshot.selectedMenu !== undefined) setSelectedMenu(atomSnapshot.selectedMenu)
if (atomSnapshot.currentMenu !== undefined) setCurrentMenu(atomSnapshot.currentMenu)
+ // undo/redo 시 복원해야 할 팝업 정보
+ setPendingPopup(atomSnapshot.pendingPopup ?? null)
}, [])
// canvas.toJSON(SAVE_KEY)의 결과를 순환 참조 없이 안전하게 직렬화
@@ -281,7 +285,7 @@ export function useUndoRedo() {
}
}, [canvas])
- const saveSnapshot = useCallback(() => {
+ const saveSnapshot = useCallback((atomOverrides = null) => {
if (!canvas || _isRestoring) return
// snapshot 전에 mouseLine 제거
@@ -293,6 +297,11 @@ export function useUndoRedo() {
const atomSnapshot = getAtomSnapshot()
+ // atomOverrides 로 비동기 state 반영이 안 된 값을 즉시 덮어쓸 수 있다
+ if (atomOverrides) {
+ Object.assign(atomSnapshot, atomOverrides)
+ }
+
const snapshot = {
canvasJSON,
atomSnapshot,
@@ -329,9 +338,8 @@ export function useUndoRedo() {
const newUndoStack = [...currentUndoStack]
const snapshot = newUndoStack.pop()
- if (snapshot.atomSnapshot?.currentMenu !== currentMenu) {
- closeAllPopups()
- }
+ // undo/redo 시 항상 팝업을 닫고 복원한다
+ closeAllPopups()
setUndoStack(newUndoStack)
setRedoStack((prev) => [...prev, currentSnapshot])
@@ -355,9 +363,8 @@ export function useUndoRedo() {
const newRedoStack = [...currentRedoStack]
const snapshot = newRedoStack.pop()
- if (snapshot.atomSnapshot?.currentMenu !== currentMenu) {
- closeAllPopups()
- }
+ // undo/redo 시 항상 팝업을 닫고 복원한다
+ closeAllPopups()
setRedoStack(newRedoStack)
setUndoStack((prev) => [...prev, currentSnapshot])
diff --git a/src/store/popupAtom.js b/src/store/popupAtom.js
index 72acaee5..fd430afd 100644
--- a/src/store/popupAtom.js
+++ b/src/store/popupAtom.js
@@ -39,3 +39,9 @@ export const promisePopupState = atom({
key: 'promisePopupStore',
default: false,
})
+
+/** undo/redo 시 복원해야 할 팝업 정보 (JSX 직렬화 불가 → 타입+파라미터만 저장) */
+export const pendingPopupState = atom({
+ key: 'pendingPopupState',
+ default: null, // { type: 'RoofAllocation', id, pos } 등
+})