From 49da64140c05055eff7319d7263acfe7189ced1f Mon Sep 17 00:00:00 2001
From: Vishnu <vishnu@hexrfactory.com>
Date: Fri, 9 May 2025 10:18:13 +0530
Subject: [PATCH] Enhance sidebar animations: improve open/close transitions
 and add reveal effects for sidebar elements.

---
 app/src/components/ui/list/DropDownList.tsx   |   1 +
 .../collaboration/camera/collabCams.tsx       |   9 +-
 .../instances/animator/vehicleAnimator.tsx    |   3 +-
 .../visualization/zone/zoneCameraTarget.tsx   | 172 +++++++++---------
 app/src/styles/layout/sidebar.scss            |  66 ++++---
 5 files changed, 146 insertions(+), 105 deletions(-)

diff --git a/app/src/components/ui/list/DropDownList.tsx b/app/src/components/ui/list/DropDownList.tsx
index 2a77303..89971a2 100644
--- a/app/src/components/ui/list/DropDownList.tsx
+++ b/app/src/components/ui/list/DropDownList.tsx
@@ -123,6 +123,7 @@ const DropDownList: React.FC<DropDownListProps> = ({
             </div>
           )}
           <button
+            title="collapse-btn"
             className="collapse-icon option"
             style={{ transform: isOpen ? "rotate(0deg)" : "rotate(-90deg)" }}
             onClick={handleToggle}
diff --git a/app/src/modules/collaboration/camera/collabCams.tsx b/app/src/modules/collaboration/camera/collabCams.tsx
index 6ce26b0..fab6efe 100644
--- a/app/src/modules/collaboration/camera/collabCams.tsx
+++ b/app/src/modules/collaboration/camera/collabCams.tsx
@@ -12,6 +12,7 @@ import CollabUserIcon from "./collabUserIcon";
 import useModuleStore from "../../../store/useModuleStore";
 import { getAvatarColor } from "../functions/getAvatarColor";
 import { useSelectedUserStore } from "../../../store/useCollabStore";
+import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
 
 const CamModelsGroup = () => {
   const navigate = useNavigate();
@@ -21,6 +22,7 @@ const CamModelsGroup = () => {
   const { socket } = useSocketStore();
   const { activeModule } = useModuleStore();
   const { selectedUser, setSelectedUser } = useSelectedUserStore();
+  const { isPlaying } = usePlayButtonStore();
 
   // eslint-disable-next-line react-hooks/exhaustive-deps
   const loader = new GLTFLoader();
@@ -244,7 +246,8 @@ const CamModelsGroup = () => {
           object={cam}
           visible={
             selectedUser?.name !== cam.userData.userName &&
-            activeModule !== "visualization"
+            activeModule !== "visualization" &&
+            !isPlaying
           }
         >
           <Html
@@ -258,7 +261,9 @@ const CamModelsGroup = () => {
               fontFamily: "Arial, sans-serif",
               display: `${activeModule !== "visualization" ? "" : "none"}`,
               opacity: `${
-                selectedUser?.name !== cam.userData.userName ? 1 : 0
+                selectedUser?.name !== cam.userData.userName && !isPlaying
+                  ? 1
+                  : 0
               }`,
               transition: "opacity .2s ease",
             }}
diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx
index ba16302..d2ecfc3 100644
--- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx
+++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx
@@ -146,7 +146,8 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
     return (
         <>
             {currentPath.length > 0 && (
-                <group >
+                // helper
+                <group visible={false}>
                     <Line points={currentPath} color="blue" lineWidth={3} />
                     {currentPath.map((point, index) => (
                         <mesh key={index} position={point}>
diff --git a/app/src/modules/visualization/zone/zoneCameraTarget.tsx b/app/src/modules/visualization/zone/zoneCameraTarget.tsx
index c3116ef..1417e58 100644
--- a/app/src/modules/visualization/zone/zoneCameraTarget.tsx
+++ b/app/src/modules/visualization/zone/zoneCameraTarget.tsx
@@ -2,90 +2,100 @@ import { useEffect, useMemo, useRef, useState } from "react";
 import { useFrame, useThree } from "@react-three/fiber";
 import * as THREE from "three";
 import { useSelectedZoneStore } from "../../../store/visualization/useZoneStore";
-import { useEditPosition, usezonePosition, usezoneTarget } from "../../../store/store";
+import {
+  useEditPosition,
+  usezonePosition,
+  usezoneTarget,
+} from "../../../store/store";
 
 export default function ZoneCentreTarget() {
-    const { selectedZone, setSelectedZone } = useSelectedZoneStore();
-    const [previousZoneCentre, setPreviousZoneCentre] = useState<number[] | null>(null);
-    const sphereRef = useRef<THREE.Mesh>(null);
-    const { camera, controls }: any = useThree();
-    const { zonePosition, setZonePosition } = usezonePosition();
-    const { zoneTarget, setZoneTarget } = usezoneTarget();
-    const { Edit, setEdit } = useEditPosition();
+  const { selectedZone } = useSelectedZoneStore();
+  const [previousZoneCentre, setPreviousZoneCentre] = useState<number[] | null>(
+    null
+  );
+  const sphereRef = useRef<THREE.Mesh>(null);
+  const { controls }: any = useThree();
+  const { setZonePosition } = usezonePosition();
+  const { setZoneTarget } = usezoneTarget();
+  const { Edit } = useEditPosition();
 
-    useEffect(() => {
-        if (
-            selectedZone.zoneViewPortTarget &&
-            JSON.stringify(previousZoneCentre) !== JSON.stringify(selectedZone.zoneViewPortTarget)
-        ) {
-            setPreviousZoneCentre(selectedZone.zoneViewPortTarget);
-        }
-    }, [selectedZone.zoneViewPortTarget, previousZoneCentre]);
+  useEffect(() => {
+    if (
+      selectedZone.zoneViewPortTarget &&
+      JSON.stringify(previousZoneCentre) !==
+        JSON.stringify(selectedZone.zoneViewPortTarget)
+    ) {
+      setPreviousZoneCentre(selectedZone.zoneViewPortTarget);
+    }
+  }, [selectedZone.zoneViewPortTarget, previousZoneCentre]);
 
-    const centrePoint = useMemo(() => {
-        if (!previousZoneCentre || !selectedZone.zoneViewPortTarget) return null;
-        return previousZoneCentre.map((value, index) =>
-            (value + selectedZone.zoneViewPortTarget[index]) / 2
-        );
-    }, [previousZoneCentre, selectedZone.zoneViewPortTarget]);
-
-    useEffect(() => {
-        if (selectedZone.zoneName !== "") {
-            if (sphereRef.current) {
-                sphereRef.current.position.set(selectedZone.zoneViewPortTarget[0], selectedZone.zoneViewPortTarget[1], selectedZone.zoneViewPortTarget[2]);
-            }
-            if (centrePoint) {
-
-                if (centrePoint.length > 0) {
-
-                    // let camPosition = new THREE.Vector3(...selectedZone.zoneViewPortPosition);
-                    // let CamTarget = new THREE.Vector3(...selectedZone.zoneViewPortTarget);
-
-                    // const direction = new THREE.Vector3().subVectors(CamTarget, camPosition).normalize();
-
-                    // const worldUp = new THREE.Vector3(0, 0, 1);
-                    // const right = new THREE.Vector3().crossVectors(worldUp, direction).normalize();
-                    // const up = new THREE.Vector3().crossVectors(direction, right).normalize();
-                    // const offsetPosition = up.clone().multiplyScalar(20);
-                    // camPosition.add(offsetPosition);
-
-                    const setCam = async () => {
-                        controls.setLookAt(centrePoint[0], 100, centrePoint[2], ...centrePoint, true);
-                        setTimeout(() => {
-                            controls?.setLookAt(
-                                ...selectedZone.zoneViewPortPosition,
-                                selectedZone.zoneViewPortTarget[0],
-                                selectedZone.zoneViewPortTarget[1],
-                                selectedZone.zoneViewPortTarget[2],
-                                true
-                            );
-                        }, 400)
-                    };
-                    setCam();
-                } else {
-
-                    const setCam = async () => {
-                        controls?.setLookAt(
-                            ...selectedZone.zoneViewPortPosition,
-                            selectedZone.zoneViewPortTarget[0],
-                            selectedZone.zoneViewPortTarget[1],
-                            selectedZone.zoneViewPortTarget[2],
-                            true
-                        );
-                    };
-                    setCam();
-                }
-            }
-        }
-    }, [selectedZone.zoneViewPortTarget]);
-
-    useFrame(() => {
-        if (Edit) {
-            setZonePosition([controls.getPosition().x, controls.getPosition().y, controls.getPosition().z])
-            setZoneTarget([controls.getTarget().x, controls.getTarget().y, controls.getTarget().z])
-        }
-    })
-    return (
-        <> </>
+  const centrePoint = useMemo(() => {
+    if (!previousZoneCentre || !selectedZone.zoneViewPortTarget) return null;
+    return previousZoneCentre.map(
+      (value, index) => (value + selectedZone.zoneViewPortTarget[index]) / 2
     );
+  }, [previousZoneCentre, selectedZone.zoneViewPortTarget]);
+
+  useEffect(() => {
+    if (selectedZone.zoneName !== "") {
+      if (sphereRef.current) {
+        sphereRef.current.position.set(
+          selectedZone.zoneViewPortTarget[0],
+          selectedZone.zoneViewPortTarget[1],
+          selectedZone.zoneViewPortTarget[2]
+        );
+      }
+      if (centrePoint) {
+        if (centrePoint.length > 0) {
+          const setCam = async () => {
+            controls.setLookAt(
+              centrePoint[0],
+              100,
+              centrePoint[2],
+              ...centrePoint,
+              true
+            );
+            setTimeout(() => {
+              controls?.setLookAt(
+                ...selectedZone.zoneViewPortPosition,
+                selectedZone.zoneViewPortTarget[0],
+                selectedZone.zoneViewPortTarget[1],
+                selectedZone.zoneViewPortTarget[2],
+                true
+              );
+            }, 400);
+          };
+          setCam();
+        } else {
+          const setCam = async () => {
+            controls?.setLookAt(
+              ...selectedZone.zoneViewPortPosition,
+              selectedZone.zoneViewPortTarget[0],
+              selectedZone.zoneViewPortTarget[1],
+              selectedZone.zoneViewPortTarget[2],
+              true
+            );
+          };
+          setCam();
+        }
+      }
+    }
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [selectedZone.zoneViewPortTarget]);
+
+  useFrame(() => {
+    if (Edit) {
+      setZonePosition([
+        controls.getPosition().x,
+        controls.getPosition().y,
+        controls.getPosition().z,
+      ]);
+      setZoneTarget([
+        controls.getTarget().x,
+        controls.getTarget().y,
+        controls.getTarget().z,
+      ]);
+    }
+  });
+  return <></>;
 }
diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss
index 6e1889e..f14d769 100644
--- a/app/src/styles/layout/sidebar.scss
+++ b/app/src/styles/layout/sidebar.scss
@@ -1437,9 +1437,11 @@
           width: 100%;
           height: 100%;
           font-size: var(--font-size-regular);
-          background: linear-gradient(0deg,
-              rgba(37, 24, 51, 0) 0%,
-              rgba(52, 41, 61, 0.5) 100%);
+          background: linear-gradient(
+            0deg,
+            rgba(37, 24, 51, 0) 0%,
+            rgba(52, 41, 61, 0.5) 100%
+          );
           pointer-events: none;
           backdrop-filter: blur(8px);
           opacity: 0;
@@ -1480,26 +1482,48 @@
 
 .sidebar-left-wrapper,
 .sidebar-right-wrapper {
-  height: calc(54vh + 150px);
   transition: height 0.2s ease-in-out;
-  .sidebar-left-container {
-    height: 100%;
-    .sidebar-left-content-container{
-      max-height: 80%;
-      .widget-left-sideBar{
-        height: 80%;
-        .widget2D.widgets-wrapper{
-
-          min-height: 50vh;
-          height: 60%;
-
-        }
-      }
-    }
-  }
 }
 
 .sidebar-left-wrapper.closed,
 .sidebar-right-wrapper.closed {
-  height: 52px;
-}
\ No newline at end of file
+  animation: closeSidebar 0.2s linear forwards;
+}
+
+.sidebar-left-wrapper.open,
+.sidebar-right-wrapper.open {
+  height: fit-content;
+  animation: openSidebar 0.2s linear;
+  .sidebar-right-container,
+  .sidebar-left-container {
+    opacity: 0;
+    animation: revealSmooth 0.3s 0.1s linear forwards;
+  }
+}
+
+@keyframes revealSmooth {
+  from {
+    opacity: 0;
+  }
+  to {
+    opacity: 1;
+  }
+}
+
+@keyframes closeSidebar{
+  from{
+    height: 60%;
+  }
+  to{
+    height: 52px;
+  }
+}
+
+@keyframes openSidebar{
+  from{
+    height: 52px;
+  }
+  to{
+    height: 60%;
+  }
+}