외벽선, 배치면 Redo undo 수정
This commit is contained in:
parent
2074c4aa93
commit
80720fef2a
@ -9,6 +9,7 @@ import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
|
|||||||
import { subMenusState } from '@/store/menuAtom'
|
import { subMenusState } from '@/store/menuAtom'
|
||||||
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||||
import { commonUtilsState } from '@/store/commonUtilsAtom'
|
import { commonUtilsState } from '@/store/commonUtilsAtom'
|
||||||
|
import { isUndoRedoRestoring } from '@/hooks/useUndoRedo'
|
||||||
|
|
||||||
export default function MenuDepth01() {
|
export default function MenuDepth01() {
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
@ -29,6 +30,10 @@ export default function MenuDepth01() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// undo/redo 복원 중에는 메뉴 변경에 따른 팝업 자동 오픈을 건너뛴다.
|
||||||
|
// 그렇지 않으면 그리기 팝업이 다시 열리며 setDrawing(true) 가 호출되어
|
||||||
|
// redo 버튼이 비활성화되는 문제가 발생한다.
|
||||||
|
if (isUndoRedoRestoring()) return
|
||||||
handleMenu(selectedMenu)
|
handleMenu(selectedMenu)
|
||||||
canvas?.discardActiveObject()
|
canvas?.discardActiveObject()
|
||||||
}, [currentMenu])
|
}, [currentMenu])
|
||||||
|
|||||||
@ -63,7 +63,7 @@ export function useOuterLineWall(id, propertiesId) {
|
|||||||
const { tempGridMode } = useTempGrid()
|
const { tempGridMode } = useTempGrid()
|
||||||
const { addPolygonByLines } = usePolygon()
|
const { addPolygonByLines } = usePolygon()
|
||||||
const { handleSelectableObjects } = useObject()
|
const { handleSelectableObjects } = useObject()
|
||||||
const { saveSnapshot } = useUndoRedo()
|
const { saveSnapshot, setDrawing } = useUndoRedo()
|
||||||
|
|
||||||
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
|
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
|
||||||
const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState)
|
const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState)
|
||||||
@ -121,6 +121,15 @@ export function useOuterLineWall(id, propertiesId) {
|
|||||||
}
|
}
|
||||||
}, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode])
|
}, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode])
|
||||||
|
|
||||||
|
// 외벽선 그리기 진행 중에는 undo/redo/스냅샷을 차단한다.
|
||||||
|
// 그리기 전 스냅샷은 첫 클릭(mouseDown) 에서 force 로 저장한다 (기존 동작 유지).
|
||||||
|
useEffect(() => {
|
||||||
|
setDrawing(true)
|
||||||
|
return () => {
|
||||||
|
setDrawing(false)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
arrow1Ref.current = arrow1
|
arrow1Ref.current = arrow1
|
||||||
}, [arrow1])
|
}, [arrow1])
|
||||||
@ -158,7 +167,9 @@ export function useOuterLineWall(id, propertiesId) {
|
|||||||
pointer = { x: Big(pointer.x).toNumber(), y: Big(pointer.y).toNumber() }
|
pointer = { x: Big(pointer.x).toNumber(), y: Big(pointer.y).toNumber() }
|
||||||
|
|
||||||
if (points.length === 0) {
|
if (points.length === 0) {
|
||||||
saveSnapshot()
|
// 그리기 진행 중에는 스냅샷이 차단되므로 force 로 저장한다.
|
||||||
|
// (처음 한 번만 저장되고 이후 추가 클릭은 points.length > 0 분기로 빠지므로 중복 저장 안 됨)
|
||||||
|
saveSnapshot(null, { force: true })
|
||||||
setPoints((prev) => [...prev, pointer])
|
setPoints((prev) => [...prev, pointer])
|
||||||
} else {
|
} else {
|
||||||
const lastPoint = points[points.length - 1]
|
const lastPoint = points[points.length - 1]
|
||||||
|
|||||||
@ -51,7 +51,7 @@ export function usePlacementShapeDrawing(id) {
|
|||||||
const { setSurfaceShapePattern } = useRoofFn()
|
const { setSurfaceShapePattern } = useRoofFn()
|
||||||
const { changeSurfaceLineType } = useSurfaceShapeBatch({})
|
const { changeSurfaceLineType } = useSurfaceShapeBatch({})
|
||||||
const { handleSelectableObjects } = useObject()
|
const { handleSelectableObjects } = useObject()
|
||||||
const { saveSnapshot, popLastSnapshot } = useUndoRedo()
|
const { saveSnapshot, popLastSnapshot, setDrawing } = useUndoRedo()
|
||||||
const { swalFire } = useSwal()
|
const { swalFire } = useSwal()
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
|
|
||||||
@ -98,12 +98,15 @@ export function usePlacementShapeDrawing(id) {
|
|||||||
}, [verticalHorizontalMode, points, adsorptionRange, adsorptionPointMode])
|
}, [verticalHorizontalMode, points, adsorptionRange, adsorptionPointMode])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 배치면 그리기 시작 시 스냅샷 저장 (undo 하면 그리기 전 상태로 복원)
|
// 배치면 그리기 시작 시 스냅샷 저장 (undo 하면 그리기 전 상태로 복원).
|
||||||
saveSnapshot()
|
// 그리기 진행 중에는 undo/redo/snapshot 을 차단하므로 force 로 저장한다.
|
||||||
|
setDrawing(true)
|
||||||
|
saveSnapshot(null, { force: true })
|
||||||
setPoints([])
|
setPoints([])
|
||||||
setType(OUTER_LINE_TYPE.OUTER_LINE)
|
setType(OUTER_LINE_TYPE.OUTER_LINE)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
setDrawing(false)
|
||||||
const placementShapeDrawingStartPoint = canvas.getObjects().find((obj) => obj.name === 'placementShapeDrawingStartPoint')
|
const placementShapeDrawingStartPoint = canvas.getObjects().find((obj) => obj.name === 'placementShapeDrawingStartPoint')
|
||||||
canvas.remove(placementShapeDrawingStartPoint)
|
canvas.remove(placementShapeDrawingStartPoint)
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {
|
|||||||
moduleSetupSurfaceState,
|
moduleSetupSurfaceState,
|
||||||
redoStackState,
|
redoStackState,
|
||||||
roofPolygonArrayState,
|
roofPolygonArrayState,
|
||||||
|
undoRedoDrawingState,
|
||||||
undoStackState,
|
undoStackState,
|
||||||
} from '@/store/canvasAtom'
|
} from '@/store/canvasAtom'
|
||||||
import { outerLinePointsState } from '@/store/outerLineAtom'
|
import { outerLinePointsState } from '@/store/outerLineAtom'
|
||||||
@ -25,6 +26,10 @@ const MAX_HISTORY = 3
|
|||||||
let _isRestoring = false
|
let _isRestoring = false
|
||||||
let _restoreTimer = null
|
let _restoreTimer = null
|
||||||
|
|
||||||
|
// 외벽선/배치면 등 그리기 진행 중에는 undo/redo/snapshot 을 차단한다.
|
||||||
|
// (확정 시점부터 history 가 동작해야 하므로)
|
||||||
|
let _isDrawing = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 외부 훅/모듈에서 현재 undo/redo 복원 중인지 확인할 때 사용
|
* 외부 훅/모듈에서 현재 undo/redo 복원 중인지 확인할 때 사용
|
||||||
*/
|
*/
|
||||||
@ -32,6 +37,21 @@ export function isUndoRedoRestoring() {
|
|||||||
return _isRestoring
|
return _isRestoring
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 외벽선/배치면 등 그리기 진행 상태를 외부에서 설정한다.
|
||||||
|
* 그리기가 시작될 때 true, 확정 또는 취소 시 false 로 설정한다.
|
||||||
|
*/
|
||||||
|
export function setUndoRedoDrawing(value) {
|
||||||
|
_isDrawing = !!value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 외부 훅/모듈에서 현재 그리기 진행 중인지 확인할 때 사용
|
||||||
|
*/
|
||||||
|
export function isUndoRedoDrawing() {
|
||||||
|
return _isDrawing
|
||||||
|
}
|
||||||
|
|
||||||
// loadFromJSON 공통 처리 - 이벤트 일시 중단 후 복원
|
// loadFromJSON 공통 처리 - 이벤트 일시 중단 후 복원
|
||||||
function _restoreFromJSON(canvas, snapshot, restoreAtomSnapshot, rebuildCanvasAtoms, setCanvasZoom) {
|
function _restoreFromJSON(canvas, snapshot, restoreAtomSnapshot, rebuildCanvasAtoms, setCanvasZoom) {
|
||||||
// 진행 중인 복원 타이머가 있으면 취소
|
// 진행 중인 복원 타이머가 있으면 취소
|
||||||
@ -98,6 +118,7 @@ export function useUndoRedo() {
|
|||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
const [undoStack, setUndoStack] = useRecoilState(undoStackState)
|
const [undoStack, setUndoStack] = useRecoilState(undoStackState)
|
||||||
const [redoStack, setRedoStack] = useRecoilState(redoStackState)
|
const [redoStack, setRedoStack] = useRecoilState(redoStackState)
|
||||||
|
const [isDrawing, setIsDrawing] = useRecoilState(undoRedoDrawingState)
|
||||||
|
|
||||||
const [, setModuleSetupSurface] = useRecoilState(moduleSetupSurfaceState)
|
const [, setModuleSetupSurface] = useRecoilState(moduleSetupSurfaceState)
|
||||||
const [moduleIsSetup, setModuleIsSetup] = useRecoilState(moduleIsSetupState)
|
const [moduleIsSetup, setModuleIsSetup] = useRecoilState(moduleIsSetupState)
|
||||||
@ -375,6 +396,9 @@ export function useUndoRedo() {
|
|||||||
// 지붕형상 수동설정 메뉴에서는 스냅샷을 쌓지 않는다 (force 시 예외)
|
// 지붕형상 수동설정 메뉴에서는 스냅샷을 쌓지 않는다 (force 시 예외)
|
||||||
if (!force && currentMenu === MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS) return
|
if (!force && currentMenu === MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS) return
|
||||||
|
|
||||||
|
// 외벽선/배치면 등 그리기 진행 중에는 스냅샷을 쌓지 않는다 (force 시 예외)
|
||||||
|
if (!force && (_isDrawing || isDrawing)) return
|
||||||
|
|
||||||
// snapshot 전에 mouseLine 제거
|
// snapshot 전에 mouseLine 제거
|
||||||
const mouseLines = canvas.getObjects().filter((obj) => obj.name === 'mouseLine')
|
const mouseLines = canvas.getObjects().filter((obj) => obj.name === 'mouseLine')
|
||||||
mouseLines.forEach((obj) => canvas.remove(obj))
|
mouseLines.forEach((obj) => canvas.remove(obj))
|
||||||
@ -402,7 +426,7 @@ export function useUndoRedo() {
|
|||||||
return newStack
|
return newStack
|
||||||
})
|
})
|
||||||
setRedoStack([])
|
setRedoStack([])
|
||||||
}, [canvas, currentMenu, getCanvasSnapshot, getAtomSnapshot])
|
}, [canvas, currentMenu, isDrawing, getCanvasSnapshot, getAtomSnapshot])
|
||||||
|
|
||||||
const closeAllPopups = useCallback(() => {
|
const closeAllPopups = useCallback(() => {
|
||||||
setPopup((prev) => ({
|
setPopup((prev) => ({
|
||||||
@ -416,6 +440,9 @@ export function useUndoRedo() {
|
|||||||
// 지붕형상 수동설정 중에는 undo 차단
|
// 지붕형상 수동설정 중에는 undo 차단
|
||||||
if (currentMenu === MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS) return
|
if (currentMenu === MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS) return
|
||||||
|
|
||||||
|
// 외벽선/배치면 등 그리기 진행 중에는 undo 차단
|
||||||
|
if (_isDrawing || isDrawing) return
|
||||||
|
|
||||||
const currentUndoStack = undoStackRef.current
|
const currentUndoStack = undoStackRef.current
|
||||||
if (!canvas || currentUndoStack.length === 0 || _isRestoring) return
|
if (!canvas || currentUndoStack.length === 0 || _isRestoring) return
|
||||||
|
|
||||||
@ -439,12 +466,15 @@ export function useUndoRedo() {
|
|||||||
setRedoStack((prev) => [...prev, currentSnapshot])
|
setRedoStack((prev) => [...prev, currentSnapshot])
|
||||||
|
|
||||||
_restoreFromJSON(canvas, snapshot, restoreAtomSnapshot, rebuildCanvasAtoms, setCanvasZoom)
|
_restoreFromJSON(canvas, snapshot, restoreAtomSnapshot, rebuildCanvasAtoms, setCanvasZoom)
|
||||||
}, [canvas, currentMenu, getCanvasSnapshot, getAtomSnapshot, restoreAtomSnapshot, rebuildCanvasAtoms, closeAllPopups])
|
}, [canvas, currentMenu, isDrawing, getCanvasSnapshot, getAtomSnapshot, restoreAtomSnapshot, rebuildCanvasAtoms, closeAllPopups])
|
||||||
|
|
||||||
const redo = useCallback(() => {
|
const redo = useCallback(() => {
|
||||||
// 지붕형상 수동설정 중에는 redo 차단
|
// 지붕형상 수동설정 중에는 redo 차단
|
||||||
if (currentMenu === MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS) return
|
if (currentMenu === MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS) return
|
||||||
|
|
||||||
|
// 외벽선/배치면 등 그리기 진행 중에는 redo 차단
|
||||||
|
if (_isDrawing || isDrawing) return
|
||||||
|
|
||||||
const currentRedoStack = redoStackRef.current
|
const currentRedoStack = redoStackRef.current
|
||||||
if (!canvas || currentRedoStack.length === 0 || _isRestoring) return
|
if (!canvas || currentRedoStack.length === 0 || _isRestoring) return
|
||||||
|
|
||||||
@ -467,7 +497,7 @@ export function useUndoRedo() {
|
|||||||
setUndoStack((prev) => [...prev, currentSnapshot])
|
setUndoStack((prev) => [...prev, currentSnapshot])
|
||||||
|
|
||||||
_restoreFromJSON(canvas, snapshot, restoreAtomSnapshot, rebuildCanvasAtoms, setCanvasZoom)
|
_restoreFromJSON(canvas, snapshot, restoreAtomSnapshot, rebuildCanvasAtoms, setCanvasZoom)
|
||||||
}, [canvas, currentMenu, getCanvasSnapshot, getAtomSnapshot, restoreAtomSnapshot, rebuildCanvasAtoms, closeAllPopups])
|
}, [canvas, currentMenu, isDrawing, getCanvasSnapshot, getAtomSnapshot, restoreAtomSnapshot, rebuildCanvasAtoms, closeAllPopups])
|
||||||
|
|
||||||
const clearStacks = useCallback(() => {
|
const clearStacks = useCallback(() => {
|
||||||
setUndoStack([])
|
setUndoStack([])
|
||||||
@ -479,13 +509,27 @@ export function useUndoRedo() {
|
|||||||
setUndoStack((prev) => (prev.length > 0 ? prev.slice(0, -1) : prev))
|
setUndoStack((prev) => (prev.length > 0 ? prev.slice(0, -1) : prev))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 외벽선/배치면 등 그리기 진행 중에는 undo/redo 를 차단한다.
|
||||||
|
* 마운트 시 true, 언마운트 시 false 로 설정한다.
|
||||||
|
*/
|
||||||
|
const setDrawing = useCallback(
|
||||||
|
(value) => {
|
||||||
|
const v = !!value
|
||||||
|
setIsDrawing(v)
|
||||||
|
setUndoRedoDrawing(v)
|
||||||
|
},
|
||||||
|
[setIsDrawing],
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
saveSnapshot,
|
saveSnapshot,
|
||||||
undo,
|
undo,
|
||||||
redo,
|
redo,
|
||||||
clearStacks,
|
clearStacks,
|
||||||
popLastSnapshot,
|
popLastSnapshot,
|
||||||
canUndo: undoStack.length > 0,
|
setDrawing,
|
||||||
canRedo: redoStack.length > 0,
|
canUndo: undoStack.length > 0 && !isDrawing,
|
||||||
|
canRedo: redoStack.length > 0 && !isDrawing,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -421,3 +421,10 @@ export const redoStackState = atom({
|
|||||||
default: [],
|
default: [],
|
||||||
dangerouslyAllowMutability: true,
|
dangerouslyAllowMutability: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 외벽선/배치면 등 그리기 진행 중 여부.
|
||||||
|
// true 동안에는 undo/redo 가 비활성화되고 새 스냅샷이 쌓이지 않는다.
|
||||||
|
export const undoRedoDrawingState = atom({
|
||||||
|
key: 'undoRedoDrawingState',
|
||||||
|
default: false,
|
||||||
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user