feat: Enhance undo/redo functionality in MoveControls2D and SelectionControls2D; process deleted and updated aisles, walls, floors, and zones for better state management

This commit is contained in:
2025-07-31 09:53:49 +05:30
parent e405596d9e
commit d6f8a29b83
2 changed files with 212 additions and 49 deletions

View File

@@ -38,10 +38,10 @@ function MoveControls2D({
const { selectedVersion } = selectedVersionStore();
const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext();
const { push2D } = undoRedo2DStore();
const { setPosition: setAislePosition, getAislesByPointId } = aisleStore();
const { setPosition: setWallPosition, getWallsByPointId } = wallStore();
const { setPosition: setFloorPosition, getFloorsByPointId } = floorStore();
const { setPosition: setZonePosition, getZonesByPointId } = zoneStore();
const { setPosition: setAislePosition, getAislesByPointId, getAisleById } = aisleStore();
const { setPosition: setWallPosition, getWallsByPointId, getWallById } = wallStore();
const { setPosition: setFloorPosition, getFloorsByPointId, getFloorById } = floorStore();
const { setPosition: setZonePosition, getZonesByPointId, getZoneById } = zoneStore();
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
const [initialPositions, setInitialPositions] = useState<Record<string, THREE.Vector3>>({});
const [initialStates, setInitialStates] = useState<Record<string, { position: THREE.Vector3; rotation?: THREE.Euler; }>>({});
@@ -225,6 +225,10 @@ function MoveControls2D({
if (movedObjects.length === 0) return;
const undoPoints: UndoRedo2DDataTypeSchema[] = [];
const processedAisles: UndoRedo2DDataTypeSchema[] = [];
const processedWalls: UndoRedo2DDataTypeSchema[] = [];
const processedFloors: UndoRedo2DDataTypeSchema[] = [];
const processedZones: UndoRedo2DDataTypeSchema[] = [];
movedObjects.forEach((movedObject: THREE.Object3D) => {
if (movedObject.userData.pointUuid) {
@@ -253,7 +257,7 @@ function MoveControls2D({
const old = initialStates[movedObject.uuid];
if (old) {
undoPoints.push({
processedAisles.push({
type: 'Aisle',
lineData: {
...updatedAisle,
@@ -293,7 +297,7 @@ function MoveControls2D({
const old = initialStates[movedObject.uuid];
if (old) {
undoPoints.push({
processedWalls.push({
type: 'Wall',
lineData: {
...updatedWall,
@@ -313,6 +317,7 @@ function MoveControls2D({
});
}
} else if (point.pointType === 'Floor') {
const Floors = getFloorsByPointId(point.pointUuid);
const updatedFloors = getFloorsByPointId(point.pointUuid);
if (updatedFloors?.length && projectId) {
updatedFloors.forEach(updatedFloor => {
@@ -331,25 +336,27 @@ function MoveControls2D({
organization
});
const old = initialStates[movedObject.uuid];
if (old) {
undoPoints.push({
type: 'Floor',
lineData: {
...updatedFloor,
points: updatedFloor.points.map(p =>
p.pointUuid === point.pointUuid
? { ...p, position: [old.position.x, old.position.y, old.position.z] }
: p
),
},
newData: updatedFloor,
const updatedFloorsData = updatedFloors.map((floor) => {
const originalFloor = Floors.find(f => f.floorUuid === floor.floorUuid) || floor;
const updatedPoints = originalFloor.points.map((pt: Point) => {
const init = initialStates[pt.pointUuid];
return init ? { ...pt, position: [init.position.x, init.position.y, init.position.z] } : pt;
}) as [Point, Point];
return {
type: "Floor" as const,
lineData: { ...originalFloor, points: updatedPoints },
newData: floor,
timeStamp: new Date().toISOString(),
});
}
};
});
processedFloors.push(...updatedFloorsData);
});
}
} else if (point.pointType === 'Zone') {
const Zones = getZonesByPointId(point.pointUuid);
const updatedZones = getZonesByPointId(point.pointUuid);
if (updatedZones?.length && projectId) {
updatedZones.forEach(updatedZone => {
@@ -368,39 +375,150 @@ function MoveControls2D({
organization
});
const old = initialStates[movedObject.uuid];
if (old) {
undoPoints.push({
type: 'Zone',
lineData: {
...updatedZone,
points: updatedZone.points.map(p =>
p.pointUuid === point.pointUuid
? { ...p, position: [old.position.x, old.position.y, old.position.z] }
: p
),
},
newData: updatedZone,
const updatedZonesData = updatedZones.map((zone) => {
const originalZone = Zones.find(z => z.zoneUuid === zone.zoneUuid) || zone;
const updatedPoints = originalZone.points.map((pt: Point) => {
const init = initialStates[pt.pointUuid];
return init ? { ...pt, position: [init.position.x, init.position.y, init.position.z] } : pt;
}) as [Point, Point];
return {
type: "Zone" as const,
lineData: { ...originalZone, points: updatedPoints },
newData: zone,
timeStamp: new Date().toISOString(),
});
}
};
});
processedZones.push(...updatedZonesData);
});
}
}
}
});
if (undoPoints.length > 0) {
push2D({
type: 'Draw',
actions: [
{
actionType: 'Lines-Update',
points: undoPoints
setTimeout(() => {
if (processedWalls.length > 0) {
const wallMap = new Map<string, UndoRedo2DDataTypeSchema[]>();
for (const wall of processedWalls) {
if (wall.type !== 'Wall' || !wall.lineData.wallUuid) continue;
const uuid = wall.lineData.wallUuid;
if (!wallMap.has(uuid)) wallMap.set(uuid, []);
wallMap.get(uuid)!.push(wall);
}
wallMap.forEach((actions, uuid) => {
const hasUpdate = actions.some(action => 'newData' in action);
if (hasUpdate) {
const wallData = getWallById(uuid);
if (wallData) {
undoPoints.push({
type: 'Wall',
lineData: actions[0].lineData as Wall,
newData: wallData as Wall,
timeStamp: new Date().toISOString()
});
}
}
]
});
}
});
}
if (processedAisles.length > 0) {
const aisleMap = new Map<string, UndoRedo2DDataTypeSchema[]>();
for (const aisle of processedAisles) {
if (aisle.type !== 'Aisle' || !aisle.lineData.aisleUuid) continue;
const uuid = aisle.lineData.aisleUuid;
if (!aisleMap.has(uuid)) aisleMap.set(uuid, []);
aisleMap.get(uuid)!.push(aisle);
}
aisleMap.forEach((actions, uuid) => {
const hasUpdate = actions.some(action => 'newData' in action);
if (hasUpdate) {
const aisleData = getAisleById(uuid);
if (aisleData) {
undoPoints.push({
type: 'Aisle',
lineData: actions[0].lineData as Aisle,
newData: aisleData as Aisle,
timeStamp: new Date().toISOString()
});
}
}
});
}
if (processedFloors.length > 0) {
const floorMap = new Map<string, UndoRedo2DDataTypeSchema[]>();
for (const floor of processedFloors) {
if (floor.type !== 'Floor' || !floor.lineData.floorUuid) continue;
const uuid = floor.lineData.floorUuid;
if (!floorMap.has(uuid)) {
floorMap.set(uuid, []);
}
floorMap.get(uuid)!.push(floor);
}
floorMap.forEach((actions, uuid) => {
const hasUpdate = actions.some(action => 'newData' in action);
if (hasUpdate) {
const floorData = getFloorById(uuid);
if (floorData) {
undoPoints.push({
type: 'Floor',
lineData: actions[0].lineData as Floor,
newData: floorData as Floor,
timeStamp: new Date().toISOString()
});
}
}
});
}
if (processedZones.length > 0) {
const zoneMap = new Map<string, UndoRedo2DDataTypeSchema[]>();
for (const zone of processedZones) {
if (zone.type !== 'Zone' || !zone.lineData.zoneUuid) continue;
const uuid = zone.lineData.zoneUuid;
if (!zoneMap.has(uuid)) {
zoneMap.set(uuid, []);
}
zoneMap.get(uuid)!.push(zone);
}
zoneMap.forEach((actions, uuid) => {
const hasUpdate = actions.some(action => 'newData' in action);
if (hasUpdate) {
const zoneData = getZoneById(uuid);
if (zoneData) {
undoPoints.push({
type: 'Zone',
lineData: actions[0].lineData as Zone,
newData: zoneData as Zone,
timeStamp: new Date().toISOString()
});
}
}
});
}
if (undoPoints.length > 0) {
push2D({
type: 'Draw',
actions: [
{
actionType: 'Lines-Update',
points: undoPoints
}
]
});
}
}, 0);
echo.success("Object moved!");
clearSelection();

View File

@@ -226,7 +226,8 @@ const SelectionControls2D: React.FC = () => {
const deletedPoints: UndoRedo2DDataTypeSchema[] = [];
const updatedPoints: UndoRedo2DDataTypeSchema[] = [];
const processedAisles: UndoRedo2DDataTypeSchema[] = [];
const processedWalls: UndoRedo2DDataTypeSchema[] = [];
const processedFloors: UndoRedo2DDataTypeSchema[] = [];
const processedZones: UndoRedo2DDataTypeSchema[] = [];
@@ -263,7 +264,7 @@ const SelectionControls2D: React.FC = () => {
timeStamp: new Date().toISOString(),
}));
deletedPoints.push(...removedAislesData);
processedAisles.push(...removedAislesData);
}
}
if (point.pointType === 'Wall') {
@@ -296,7 +297,7 @@ const SelectionControls2D: React.FC = () => {
timeStamp: new Date().toISOString(),
}));
deletedPoints.push(...removedWallsData);
processedWalls.push(...removedWallsData);
}
}
if (point.pointType === 'Floor') {
@@ -433,6 +434,50 @@ const SelectionControls2D: React.FC = () => {
})
setTimeout(() => {
if (processedWalls.length > 0) {
const wallMap = new Map<string, UndoRedo2DDataTypeSchema[]>();
for (const wall of processedWalls) {
if (wall.type !== 'Wall' || !wall.lineData.wallUuid) continue;
const uuid = wall.lineData.wallUuid;
if (!wallMap.has(uuid)) wallMap.set(uuid, []);
wallMap.get(uuid)!.push(wall);
}
wallMap.forEach((actions) => {
const hasDelete = actions.some(action => !('newData' in action));
if (hasDelete) {
deletedPoints.push({
type: 'Wall',
lineData: actions[0].lineData as Wall,
timeStamp: new Date().toISOString()
});
}
});
}
if (processedAisles.length > 0) {
const aisleMap = new Map<string, UndoRedo2DDataTypeSchema[]>();
for (const aisle of processedAisles) {
if (aisle.type !== 'Aisle' || !aisle.lineData.aisleUuid) continue;
const uuid = aisle.lineData.aisleUuid;
if (!aisleMap.has(uuid)) aisleMap.set(uuid, []);
aisleMap.get(uuid)!.push(aisle);
}
aisleMap.forEach((actions) => {
const hasDelete = actions.some(action => !('newData' in action));
if (hasDelete) {
deletedPoints.push({
type: 'Aisle',
lineData: actions[0].lineData as Aisle,
timeStamp: new Date().toISOString()
});
}
});
}
if (processedFloors.length > 0) {
const floorMap = new Map<string, UndoRedo2DDataTypeSchema[]>();