diff --git a/src/App.tsx b/src/App.tsx index 5b03b34..d761ec2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import { UndoRedoProvider } from "./UndoRedoContext"; +import { UndoRedoProvider } from "./components/undoRedo/UndoRedoContext"; import SceneView from "./pages/SceneView"; import "./styles/main.scss"; diff --git a/src/components/ui/OutlineList .tsx b/src/components/ui/OutlineList .tsx index a315fbe..e9169ea 100644 --- a/src/components/ui/OutlineList .tsx +++ b/src/components/ui/OutlineList .tsx @@ -4,7 +4,7 @@ import TreeNode from "./TreeNode"; import Search from "./Search"; import { AddIcon, ChevronIcon, CollapseAllIcon } from "../../icons/ExportIcons"; import type { OutlinePanelProps } from "./OutlinePanel"; -import { useUndoRedo } from "../../useUndoRedo"; +import { useUndoRedo } from "../undoRedo/useUndoRedo"; import { DEFAULT_PRAMS, type AssetGroupChild } from "../../data/OutlineListData"; @@ -21,7 +21,7 @@ export const OutlineList: React.FC = ({ width, search, data, - onDrop, + // onDrop, onDragOver, onRename, onDragStart, @@ -104,23 +104,32 @@ export const OutlineList: React.FC = ({ setDraggedNode(null); return; } - const prevHierarchy = structuredClone(hierarchy); - const updatedHierarchy = [...hierarchy]; + const updatedHierarchy = structuredClone(hierarchy); - if (!removeFromHierarchy(draggedItem, updatedHierarchy)) return; + // ✅ Determine which items to move + const itemsToMove = + selectedObjects && selectedObjects.length > 1 ? selectedObjects : [draggedItem]; - if (!insertByDropAction(draggedItem, targetId, dropAction, updatedHierarchy)) { - updatedHierarchy.push(draggedItem); + // ✅ Remove and insert each dragged item + for (const item of itemsToMove) { + if (!removeFromHierarchy(item, updatedHierarchy)) continue; + + if (!insertByDropAction(item, targetId, dropAction, updatedHierarchy)) { + updatedHierarchy.push(item); + } } + addAction({ do: () => setHierarchy(updatedHierarchy), undo: () => setHierarchy(prevHierarchy), }); setHierarchy(updatedHierarchy); - if (onDrop) onDrop(updatedHierarchy); setDraggedNode(null); + clearAllHighlights(); + setSelectedObject(null); + setSelectedObjects([]); }; const getDropZone = (y: number): DropAction => { @@ -252,12 +261,14 @@ export const OutlineList: React.FC = ({ }); const newHierarchy = updateNodeName(structuredClone(hierarchy)); + setHierarchy(newHierarchy); if (onRename) onRename(id, newName); addAction({ do: () => setHierarchy(newHierarchy), undo: () => setHierarchy(prevHierarchy), }); }; + useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.ctrlKey && e.key.toLowerCase() === "z") { diff --git a/src/components/ui/TreeNode.tsx b/src/components/ui/TreeNode.tsx index 37b12bc..ef269a5 100644 --- a/src/components/ui/TreeNode.tsx +++ b/src/components/ui/TreeNode.tsx @@ -10,6 +10,7 @@ import { LockIcon, } from "../../icons/ExportIcons"; import { DEFAULT_PRAMS, type AssetGroupChild } from "../../data/OutlineListData"; +import { useUndoRedo } from "../undoRedo/useUndoRedo"; interface TreeNodeProps { item: AssetGroupChild; @@ -30,7 +31,6 @@ interface TreeNodeProps { setSelectedObjects: React.Dispatch>; setHierarchy: React.Dispatch>; setDraggedItems: React.Dispatch>; - pushHistory?: (newHierarchy: AssetGroupChild[]) => void; } const TreeNode: React.FC = ({ @@ -50,7 +50,6 @@ const TreeNode: React.FC = ({ selectedObjects, setDraggedItems, draggedItems, - pushHistory, setHierarchy, hierarchy, }) => { @@ -59,7 +58,7 @@ const TreeNode: React.FC = ({ const [isExpanded, setIsExpanded] = useState(item.isExpanded || false); const [isEditing, setIsEditing] = useState(false); const [name, setName] = useState(item.groupName || item.modelName); - + const { addAction } = useUndoRedo(); const handleDragStart = (e: React.DragEvent) => { e.stopPropagation(); onDragStart(item); @@ -141,7 +140,7 @@ const TreeNode: React.FC = ({ const handleToggleVisibility = (e: React.MouseEvent) => { e.stopPropagation(); - + const prevHierarchy = structuredClone(hierarchy); const updatedHierarchy = structuredClone(hierarchy); const targetIds = selectedObjects.length ? selectedObjects.map((obj) => obj.modelUuid || obj.groupUuid) @@ -158,12 +157,16 @@ const TreeNode: React.FC = ({ }; toggleVisibilityRecursive(updatedHierarchy); - pushHistory?.(updatedHierarchy); setHierarchy(updatedHierarchy); + addAction({ + do: () => setHierarchy(updatedHierarchy), + undo: () => setHierarchy(prevHierarchy), + }); }; const handleToggleLock = (e: React.MouseEvent) => { e.stopPropagation(); + const prevHierarchy = structuredClone(hierarchy); const updatedHierarchy = structuredClone(hierarchy); const targetIds = selectedObjects.length ? selectedObjects.map((obj) => obj.modelUuid || obj.groupUuid) @@ -180,8 +183,12 @@ const TreeNode: React.FC = ({ }; toggleLockRecursive(updatedHierarchy); - pushHistory?.(updatedHierarchy); + setHierarchy(updatedHierarchy); + addAction({ + do: () => setHierarchy(updatedHierarchy), + undo: () => setHierarchy(prevHierarchy), + }); }; useEffect(() => { diff --git a/src/UndoRedoContext.tsx b/src/components/undoRedo/UndoRedoContext.tsx similarity index 100% rename from src/UndoRedoContext.tsx rename to src/components/undoRedo/UndoRedoContext.tsx diff --git a/src/useUndoRedo.tsx b/src/components/undoRedo/useUndoRedo.tsx similarity index 100% rename from src/useUndoRedo.tsx rename to src/components/undoRedo/useUndoRedo.tsx