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