From 5d40139e95b776b784857026b4448523a56eb056 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Fri, 11 Jul 2025 09:26:21 +0530 Subject: [PATCH] Add custom SVG cursors and update cursor handling - Introduced new SVG cursor assets: cross, default, export, move, open, pen, and pointing. - Implemented SCSS styles for cursor usage in the scene container, defining specific cursors for different modes (draw, pointer, pen, measure, hand). - Enhanced cursor functionality by adding a utility function to handle canvas cursor changes based on user actions, mapping specific actions to corresponding cursor styles. --- app/src/assets/cursors/ask.svg | 6 + app/src/assets/cursors/cell.svg | 4 + app/src/assets/cursors/close.svg | 7 + app/src/assets/cursors/cross.svg | 4 + app/src/assets/cursors/default.svg | 4 + app/src/assets/cursors/export.svg | 4 + app/src/assets/cursors/move.svg | 5 + app/src/assets/cursors/open.svg | 7 + app/src/assets/cursors/pen.svg | 8 + app/src/assets/cursors/pointing.svg | 7 + app/src/pages/Project.tsx | 242 ++++++++++++++------------- app/src/styles/main.scss | 3 +- app/src/styles/scene/cursors.scss | 47 ++++++ app/src/utils/handleCanvasCursors.ts | 31 ++++ 14 files changed, 261 insertions(+), 118 deletions(-) create mode 100644 app/src/assets/cursors/ask.svg create mode 100644 app/src/assets/cursors/cell.svg create mode 100644 app/src/assets/cursors/close.svg create mode 100644 app/src/assets/cursors/cross.svg create mode 100644 app/src/assets/cursors/default.svg create mode 100644 app/src/assets/cursors/export.svg create mode 100644 app/src/assets/cursors/move.svg create mode 100644 app/src/assets/cursors/open.svg create mode 100644 app/src/assets/cursors/pen.svg create mode 100644 app/src/assets/cursors/pointing.svg create mode 100644 app/src/styles/scene/cursors.scss create mode 100644 app/src/utils/handleCanvasCursors.ts diff --git a/app/src/assets/cursors/ask.svg b/app/src/assets/cursors/ask.svg new file mode 100644 index 0000000..39f66a1 --- /dev/null +++ b/app/src/assets/cursors/ask.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/assets/cursors/cell.svg b/app/src/assets/cursors/cell.svg new file mode 100644 index 0000000..2632892 --- /dev/null +++ b/app/src/assets/cursors/cell.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/assets/cursors/close.svg b/app/src/assets/cursors/close.svg new file mode 100644 index 0000000..6fdebed --- /dev/null +++ b/app/src/assets/cursors/close.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/src/assets/cursors/cross.svg b/app/src/assets/cursors/cross.svg new file mode 100644 index 0000000..1ac52f1 --- /dev/null +++ b/app/src/assets/cursors/cross.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/assets/cursors/default.svg b/app/src/assets/cursors/default.svg new file mode 100644 index 0000000..bf6a33b --- /dev/null +++ b/app/src/assets/cursors/default.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/assets/cursors/export.svg b/app/src/assets/cursors/export.svg new file mode 100644 index 0000000..a560635 --- /dev/null +++ b/app/src/assets/cursors/export.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/assets/cursors/move.svg b/app/src/assets/cursors/move.svg new file mode 100644 index 0000000..12b1692 --- /dev/null +++ b/app/src/assets/cursors/move.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/assets/cursors/open.svg b/app/src/assets/cursors/open.svg new file mode 100644 index 0000000..695087a --- /dev/null +++ b/app/src/assets/cursors/open.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/src/assets/cursors/pen.svg b/app/src/assets/cursors/pen.svg new file mode 100644 index 0000000..ff956b5 --- /dev/null +++ b/app/src/assets/cursors/pen.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/app/src/assets/cursors/pointing.svg b/app/src/assets/cursors/pointing.svg new file mode 100644 index 0000000..4a50a4b --- /dev/null +++ b/app/src/assets/cursors/pointing.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index 0802fcd..bd4e2bc 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -1,17 +1,14 @@ import React, { useEffect } from "react"; import useModuleStore from "../store/useModuleStore"; import { - useSocketStore, - useOrganization, - useUserName, - useWallItems, - useSaveVersion, - useViewSceneStore, - useProjectName, - useRenameModeStore, - useSelectedFloorItem, - useZones, - useSelectedComment, + useSocketStore, + useOrganization, + useUserName, + useWallItems, + useSaveVersion, + useProjectName, + useZones, + useActiveTool, } from "../store/builder/store"; import { useNavigate, useParams } from "react-router-dom"; import { useSelectedUserStore } from "../store/collaboration/useCollabStore"; @@ -30,125 +27,136 @@ import { getVersionHistoryApi } from "../services/factoryBuilder/versionControl/ import { useVersionHistoryStore } from "../store/builder/useVersionHistoryStore"; import { VersionProvider } from "../modules/builder/version/versionContext"; import { sharedWithMeProjects } from "../services/dashboard/sharedWithMeProject"; +import { handleCanvasCursors } from "../utils/handleCanvasCursors"; const Project: React.FC = () => { - let navigate = useNavigate(); - const echo = useLogger(); - const { setToggleUI } = useToggleStore(); - const { setActiveModule } = useModuleStore(); - const { setUserName } = useUserName(); - const { setOrganization } = useOrganization(); - const { setWallItems } = useWallItems(); - const { setZones } = useZones(); - const { isVersionSaved } = useSaveVersion(); - const { projectId } = useParams(); - const { setProjectName } = useProjectName(); - const { userId, email, organization, userName } = getUserData(); - const { selectedUser } = useSelectedUserStore(); - const { isLogListVisible } = useLogger(); - const { setVersions } = useVersionHistoryStore(); + let navigate = useNavigate(); + const echo = useLogger(); + const { setToggleUI } = useToggleStore(); + const { setActiveModule } = useModuleStore(); + const { setUserName } = useUserName(); + const { setOrganization } = useOrganization(); + const { setWallItems } = useWallItems(); + const { setZones } = useZones(); + const { isVersionSaved } = useSaveVersion(); + const { projectId } = useParams(); + const { setProjectName } = useProjectName(); + const { userId, email, organization, userName } = getUserData(); + const { selectedUser } = useSelectedUserStore(); + const { isLogListVisible } = useLogger(); + const { setVersions } = useVersionHistoryStore(); + const { activeTool } = useActiveTool(); + useEffect(() => { + if (!email || !userId) { + console.error("User data not found in localStorage"); + return; + } - useEffect(() => { - if (!email || !userId) { - console.error("User data not found in localStorage"); - return; - } + const fetchProjects = async () => { + try { + const projects = await getAllProjects(userId, organization); + const shared = await sharedWithMeProjects(); - const fetchProjects = async () => { - try { - const projects = await getAllProjects(userId, organization); - const shared = await sharedWithMeProjects(); + const allProjects = [...(projects?.Projects || []), ...(shared || [])]; - const allProjects = [...(projects?.Projects || []), ...(shared || [])]; + const matchedProject = allProjects.find( + (val: any) => val.projectUuid === projectId || val._id === projectId + ); - const matchedProject = allProjects.find( - (val: any) => val.projectUuid === projectId || val._id === projectId - ); + if (matchedProject) { + setProjectName(matchedProject.projectName); + await viewProject(organization, matchedProject._id, userId); + } else { + console.warn("Project not found with given ID:", projectId); + } + } catch (error) { + console.error("Error fetching projects:", error); + } + }; - if (matchedProject) { - setProjectName(matchedProject.projectName); - await viewProject(organization, matchedProject._id, userId); - } else { - console.warn("Project not found with given ID:", projectId); - } - } catch (error) { - console.error("Error fetching projects:", error); - } - }; + fetchProjects(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - fetchProjects(); - }, []); + useEffect(() => { + if (!projectId) return; + getVersionHistoryApi(projectId) + .then((data) => { + const versions: VersionHistory = []; + data.versions.forEach((version: any) => { + versions.push({ + version: version.version, + versionId: version.versionId, + versionName: version.versionName, + versionDescription: version.description, + timeStamp: version.createdAt, + createdBy: version.createdBy.userName, + }); + }); + setVersions(versions); + }) + .catch(() => { + console.error("Error fetching version history"); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [projectId]); + useEffect(() => { + if (!isVersionSaved) { + setToggleUI(true, true); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isVersionSaved]); - useEffect(() => { - if (!projectId) return; - getVersionHistoryApi(projectId).then((data) => { - const versions: VersionHistory = []; - data.versions.forEach((version: any) => { - versions.push({ - version: version.version, - versionId: version.versionId, - versionName: version.versionName, - versionDescription: version.description, - timeStamp: version.createdAt, - createdBy: version.createdBy.userName - }) - }) - setVersions(versions); - }).catch(() => { - console.error("Error fetching version history") - }) - }, [projectId]) + useEffect(() => { + setWallItems([]); + setZones([]); + setActiveModule("builder"); + if (email) { + const token = localStorage.getItem("token"); + const refreshToken = localStorage.getItem("refreshToken"); + if (token) { + useSocketStore + .getState() + .initializeSocket(email, organization, token, refreshToken); + } + if (organization && userName) { + setOrganization(organization); + setUserName(userName); + } + echo.success("Log in successful"); + } else { + navigate("/"); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - useEffect(() => { - if (!isVersionSaved) { - setToggleUI(true, true); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isVersionSaved]); + useEffect(() => { + handleCanvasCursors(activeTool); + console.log('activeTool: ', activeTool); + }, [activeTool]); - useEffect(() => { - setWallItems([]); - setZones([]); - setActiveModule("builder"); - if (email) { - const token = localStorage.getItem("token"); - const refreshToken = localStorage.getItem("refreshToken") - if (token) { - useSocketStore.getState().initializeSocket(email, organization, token, refreshToken); - } - if (organization && userName) { - setOrganization(organization); - setUserName(userName); - } - echo.success("Log in successful"); - } else { - navigate("/"); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( -
- - - - - - - - - - - {selectedUser && } - {isLogListVisible && ( - - - - )} -
- ); + return ( +
+ + + + + + + + + + + {selectedUser && } + {isLogListVisible && ( + + + + )} +
+ ); }; export default Project; diff --git a/app/src/styles/main.scss b/app/src/styles/main.scss index 3de3896..df75860 100644 --- a/app/src/styles/main.scss +++ b/app/src/styles/main.scss @@ -45,4 +45,5 @@ // @use "./scene/scene"; -@use "./scene/comments"; \ No newline at end of file +@use "./scene/comments"; +@use "./scene/cursors.scss"; diff --git a/app/src/styles/scene/cursors.scss b/app/src/styles/scene/cursors.scss new file mode 100644 index 0000000..cef9b92 --- /dev/null +++ b/app/src/styles/scene/cursors.scss @@ -0,0 +1,47 @@ +$cursor-default: url("../../assets/cursors/default.svg") 0 0, default; +$cursor-pen: url("../../assets/cursors/pen.svg") 0 0, default; +$cursor-delete: url("../../assets/cursors/pointing.svg") 4 0, default; +$cursor-draw: url("../../assets/cursors/cell.svg") 8 8, default; +$cursor-cross: url("../../assets/cursors/cross.svg") 8 8, default; +$cursor-grab: url("../../assets/cursors/open.svg") 8 8, default; +$cursor-grabing: url("../../assets/cursors/close.svg") 8 8, default; + +.scene-container { + canvas { + cursor: $cursor-default !important; + } +} + +.scene-container.draw { + canvas { + cursor: $cursor-draw !important; + } +} + +.scene-container.pointer { + canvas { + cursor: $cursor-delete !important; + } +} + +.scene-container.pen { + canvas { + cursor: $cursor-pen !important; + } +} + +.scene-container.measure { + canvas { + cursor: $cursor-cross !important; + } +} + +.scene-container.hand { + canvas { + cursor: $cursor-grab !important; + &:active{ + cursor: $cursor-grabing !important; + } + } +} + diff --git a/app/src/utils/handleCanvasCursors.ts b/app/src/utils/handleCanvasCursors.ts new file mode 100644 index 0000000..72e2bd6 --- /dev/null +++ b/app/src/utils/handleCanvasCursors.ts @@ -0,0 +1,31 @@ +export const handleCanvasCursors = (name: string) => { + const canvas = document.getElementById('work-space-three-d-canvas'); + if (!canvas) return; + + const cursorMap: Record = { + 'draw-wall': 'draw', + 'draw-aisle': 'draw', + 'draw-zone': 'draw', + 'draw-floor': 'draw', + measure: 'measure', + delete: 'pointer', + pen: 'pen', + 'free-hand': 'hand', + // Add more mappings as needed + }; + + const validCursorClasses = new Set(Object.values(cursorMap)); + + // Remove previously applied cursor-related classes + canvas.classList.forEach((cls) => { + if (validCursorClasses.has(cls)) { + canvas.classList.remove(cls); + } + }); + + // Add the new cursor class + const newCursorClass = cursorMap[name]; + if (newCursorClass) { + canvas.classList.add(newCursorClass); + } +};