From 24ff130d8273dea271eb73468b192d5ada671cf7 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Fri, 11 Jul 2025 12:31:00 +0530 Subject: [PATCH] feat: Enhance cursor handling and mouse action notes in the footer and builder components --- app/src/components/footer/Footer.tsx | 78 ++++++++++++------- app/src/modules/builder/line/line.tsx | 13 ++-- app/src/modules/builder/point/point.tsx | 15 ++-- app/src/pages/Project.tsx | 3 +- app/src/styles/scene/cursors.scss | 6 ++ .../{ => mouseUtils}/handleCanvasCursors.ts | 5 ++ app/src/utils/mouseUtils/mouseHelper.ts | 46 +++++++++++ 7 files changed, 124 insertions(+), 42 deletions(-) rename app/src/utils/{ => mouseUtils}/handleCanvasCursors.ts (85%) create mode 100644 app/src/utils/mouseUtils/mouseHelper.ts diff --git a/app/src/components/footer/Footer.tsx b/app/src/components/footer/Footer.tsx index 737ee26..fc16956 100644 --- a/app/src/components/footer/Footer.tsx +++ b/app/src/components/footer/Footer.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import { HelpIcon } from "../icons/DashboardIcon"; import { useLogger } from "../ui/log/LoggerContext"; import { GetLogIcon } from "./getLogIcons"; @@ -8,10 +8,13 @@ import { CurserRightIcon, } from "../icons/LogIcons"; import ShortcutHelper from "./shortcutHelper"; -import useVersionHistoryVisibleStore, { useShortcutStore } from "../../store/builder/store"; +import useVersionHistoryVisibleStore, { + useShortcutStore, +} from "../../store/builder/store"; import { usePlayButtonStore } from "../../store/usePlayButtonStore"; import useModuleStore, { useSubModuleStore } from "../../store/useModuleStore"; import { useVersionContext } from "../../modules/builder/version/versionContext"; +import { mouseActionHelper } from "../../utils/mouseUtils/mouseHelper"; const Footer: React.FC = () => { const { logs, setIsLogListVisible } = useLogger(); @@ -25,28 +28,45 @@ const Footer: React.FC = () => { const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); + const [notes, setNotes] = useState({ + Leftnote: "", + Middlenote: "", + Rightnote: "", + }); + + useEffect(() => { + const cleanup = mouseActionHelper(setNotes); + return () => cleanup(); + }, []); + + const mouseButtons = [ + { + icon: , + label: notes.Leftnote !== "" ? notes.Leftnote : "Pan", + mouse: "left", + }, + { + icon: , + label: notes.Middlenote !== "" ? notes.Middlenote : "Scroll Zoom", + mouse: "middle", + }, + { + icon: , + label: notes.Rightnote !== "" ? notes.Rightnote : "Orbit / Cancel action", + mouse: "right", + }, + ]; + return (
-
-
- + {mouseButtons.map(({ icon, label, mouse }) => ( +
+
{icon}
+
{label}
-
Selection
-
-
-
- -
-
Rotate/Zoom
-
-
-
- -
-
Pan/Context Menu
-
+ ))}
@@ -68,12 +88,15 @@ const Footer: React.FC = () => { )}
-
{ - setVersionHistoryVisible(true); - setSubModule("properties"); - setActiveModule('builder'); - }}> - {(selectedVersion?.version) ?? 'v 0.0.0'} +
{ + setVersionHistoryVisible(true); + setSubModule("properties"); + setActiveModule("builder"); + }} + > + {selectedVersion?.version ?? "v 0.0.0"}
@@ -83,8 +106,9 @@ const Footer: React.FC = () => { {!isPlaying && (
diff --git a/app/src/modules/builder/line/line.tsx b/app/src/modules/builder/line/line.tsx index cc53b9d..2eb05dc 100644 --- a/app/src/modules/builder/line/line.tsx +++ b/app/src/modules/builder/line/line.tsx @@ -9,6 +9,7 @@ import * as Constants from '../../../types/world/worldConstants'; import { useVersionContext } from '../version/versionContext'; import { useParams } from 'react-router-dom'; import { getUserData } from '../../../functions/getUserData'; +import { handleCanvasCursors } from '../../../utils/mouseUtils/handleCanvasCursors'; // import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi'; // import { deleteWallApi } from '../../../services/factoryBuilder/wall/deleteWallApi'; @@ -23,7 +24,7 @@ interface LineProps { function Line({ points }: Readonly) { const [isHovered, setIsHovered] = useState(false); - const { raycaster, camera, pointer, gl } = useThree(); + const { raycaster, camera, pointer } = useThree(); const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); const [isDeletable, setIsDeletable] = useState(false); const { socket } = useSocketStore(); @@ -213,7 +214,7 @@ function Line({ points }: Readonly) { }); } } - gl.domElement.style.cursor = 'default'; + handleCanvasCursors('default'); } } @@ -224,7 +225,7 @@ function Line({ points }: Readonly) { const hit = raycaster.ray.intersectPlane(plane, intersectionPoint); if (hit) { - gl.domElement.style.cursor = 'move'; + handleCanvasCursors('grabbing'); const positionWithOffset = new THREE.Vector3().addVectors(hit, dragOffset); const start = new THREE.Vector3(...points[0].position); @@ -269,7 +270,7 @@ function Line({ points }: Readonly) { const handleDragEnd = (points: [Point, Point]) => { if (toolMode !== 'move' || !dragOffset) return; - gl.domElement.style.cursor = 'default'; + handleCanvasCursors('default'); setDragOffset(null); if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') { const updatedWalls1 = getWallsByPointId(points[0].pointUuid); @@ -377,14 +378,14 @@ function Line({ points }: Readonly) { setHoveredLine(points); setIsHovered(true) if (toolMode === 'move' && !hoveredPoint) { - gl.domElement.style.cursor = 'pointer'; + handleCanvasCursors('grab'); } } }} onPointerOut={() => { if (hoveredLine) { setHoveredLine(null); - gl.domElement.style.cursor = 'default'; + handleCanvasCursors('default'); } setIsHovered(false) }} diff --git a/app/src/modules/builder/point/point.tsx b/app/src/modules/builder/point/point.tsx index 73dec77..c6ed85a 100644 --- a/app/src/modules/builder/point/point.tsx +++ b/app/src/modules/builder/point/point.tsx @@ -20,10 +20,11 @@ import { useSceneContext } from '../../scene/sceneContext'; // import { deleteZoneApi } from '../../../services/factoryBuilder/zone/deleteZoneApi'; import { getUserData } from '../../../functions/getUserData'; +import { handleCanvasCursors } from '../../../utils/mouseUtils/handleCanvasCursors'; function Point({ point }: { readonly point: Point }) { const materialRef = useRef(null); - const { raycaster, camera, pointer, gl } = useThree(); + const { raycaster, camera, pointer } = useThree(); const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); const [isHovered, setIsHovered] = useState(false); const [dragOffset, setDragOffset] = useState(null); @@ -44,7 +45,7 @@ function Point({ point }: { readonly point: Point }) { const colors = getColor(point); useEffect(() => { - gl.domElement.style.cursor = 'default'; + handleCanvasCursors('default'); }, [toolMode]) function getColor(point: Point) { @@ -114,7 +115,7 @@ function Point({ point }: { readonly point: Point }) { const hit = raycaster.ray.intersectPlane(plane, intersectionPoint); if (hit) { - gl.domElement.style.cursor = 'move'; + handleCanvasCursors('grabbing'); const positionWithOffset = new THREE.Vector3().addVectors(hit, dragOffset); const newPosition: [number, number, number] = [positionWithOffset.x, positionWithOffset.y, positionWithOffset.z]; @@ -152,7 +153,7 @@ function Point({ point }: { readonly point: Point }) { }; const handleDragEnd = (point: Point) => { - gl.domElement.style.cursor = 'default'; + handleCanvasCursors('default'); setDragOffset(null); if (toolMode !== 'move') return; if (point.pointType === 'Aisle') { @@ -396,7 +397,7 @@ function Point({ point }: { readonly point: Point }) { }); } } - gl.domElement.style.cursor = 'default'; + handleCanvasCursors('default'); } } @@ -431,14 +432,14 @@ function Point({ point }: { readonly point: Point }) { setHoveredPoint(point); setIsHovered(true); if (toolMode === 'move') { - gl.domElement.style.cursor = 'pointer'; + handleCanvasCursors('grab'); } } }} onPointerOut={() => { if (hoveredPoint) { setHoveredPoint(null); - gl.domElement.style.cursor = 'default'; + handleCanvasCursors('default'); } setIsHovered(false) }} diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index bd4e2bc..b257521 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -27,7 +27,7 @@ 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"; +import { handleCanvasCursors } from "../utils/mouseUtils/handleCanvasCursors"; const Project: React.FC = () => { let navigate = useNavigate(); @@ -134,7 +134,6 @@ const Project: React.FC = () => { useEffect(() => { handleCanvasCursors(activeTool); - console.log('activeTool: ', activeTool); }, [activeTool]); return ( diff --git a/app/src/styles/scene/cursors.scss b/app/src/styles/scene/cursors.scss index cef9b92..43f3078 100644 --- a/app/src/styles/scene/cursors.scss +++ b/app/src/styles/scene/cursors.scss @@ -45,3 +45,9 @@ $cursor-grabing: url("../../assets/cursors/close.svg") 8 8, default; } } +.scene-container.hand-closed { + canvas { + cursor: $cursor-grabing !important; + } +} + diff --git a/app/src/utils/handleCanvasCursors.ts b/app/src/utils/mouseUtils/handleCanvasCursors.ts similarity index 85% rename from app/src/utils/handleCanvasCursors.ts rename to app/src/utils/mouseUtils/handleCanvasCursors.ts index 72e2bd6..5bad3ae 100644 --- a/app/src/utils/handleCanvasCursors.ts +++ b/app/src/utils/mouseUtils/handleCanvasCursors.ts @@ -3,14 +3,19 @@ export const handleCanvasCursors = (name: string) => { if (!canvas) return; const cursorMap: Record = { + default: '', 'draw-wall': 'draw', 'draw-aisle': 'draw', 'draw-zone': 'draw', 'draw-floor': 'draw', measure: 'measure', delete: 'pointer', + pointer: 'pointer', + grab: 'hand', + grabbing: 'hand-closed', pen: 'pen', 'free-hand': 'hand', + move: 'move', // Add more mappings as needed }; diff --git a/app/src/utils/mouseUtils/mouseHelper.ts b/app/src/utils/mouseUtils/mouseHelper.ts new file mode 100644 index 0000000..94be50e --- /dev/null +++ b/app/src/utils/mouseUtils/mouseHelper.ts @@ -0,0 +1,46 @@ +const actionNotes: Record = { + 'left+CONTROL': 'Box Select', + 'left+SHIFT': 'Multi Select', + 'middle+CONTROL': 'Zoom In', +}; + +export function mouseActionHelper( + onUpdate: (notes: { + Leftnote: string; + Middlenote: string; + Rightnote: string; + }) => void +) { + const activeKeys = new Set(); + + function updateNotesFromKeys() { + const sortedKeys = Array.from(activeKeys).sort(); + const leftKey = ['left', ...sortedKeys].join('+'); + const middleKey = ['middle', ...sortedKeys].join('+'); + const rightKey = ['right', ...sortedKeys].join('+'); + + onUpdate({ + Leftnote: actionNotes[leftKey] || '', + Middlenote: actionNotes[middleKey] || '', + Rightnote: actionNotes[rightKey] || '', + }); + } + + function handleKeyDown(event: KeyboardEvent) { + activeKeys.add(event.key.toUpperCase()); + updateNotesFromKeys(); + } + + function handleKeyUp(event: KeyboardEvent) { + activeKeys.delete(event.key.toUpperCase()); + updateNotesFromKeys(); + } + + window.addEventListener('keydown', handleKeyDown); + window.addEventListener('keyup', handleKeyUp); + + return () => { + window.removeEventListener('keydown', handleKeyDown); + window.removeEventListener('keyup', handleKeyUp); + }; +}