feat: Enhance cursor handling and mouse action notes in the footer and builder components

This commit is contained in:
2025-07-11 12:31:00 +05:30
parent 5d40139e95
commit 24ff130d82
7 changed files with 124 additions and 42 deletions

View File

@@ -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: <CurserLeftIcon />,
label: notes.Leftnote !== "" ? notes.Leftnote : "Pan",
mouse: "left",
},
{
icon: <CurserMiddleIcon />,
label: notes.Middlenote !== "" ? notes.Middlenote : "Scroll Zoom",
mouse: "middle",
},
{
icon: <CurserRightIcon />,
label: notes.Rightnote !== "" ? notes.Rightnote : "Orbit / Cancel action",
mouse: "right",
},
];
return (
<div className="footer-container">
<div className="footer-wrapper">
<div className="selection-wrapper">
<div className="selector-wrapper">
<div className="icon">
<CurserLeftIcon />
{mouseButtons.map(({ icon, label, mouse }) => (
<div className="selector-wrapper" key={mouse}>
<div className="icon">{icon}</div>
<div className="selector">{label}</div>
</div>
<div className="selector">Selection</div>
</div>
<div className="selector-wrapper">
<div className="icon">
<CurserMiddleIcon />
</div>
<div className="selector">Rotate/Zoom</div>
</div>
<div className="selector-wrapper">
<div className="icon">
<CurserRightIcon />
</div>
<div className="selector">Pan/Context Menu</div>
</div>
))}
</div>
<div className="logs-wrapper">
@@ -68,12 +88,15 @@ const Footer: React.FC = () => {
)}
</button>
</div>
<div className="version" onClick={() => {
setVersionHistoryVisible(true);
setSubModule("properties");
setActiveModule('builder');
}}>
{(selectedVersion?.version) ?? 'v 0.0.0'}
<div
className="version"
onClick={() => {
setVersionHistoryVisible(true);
setSubModule("properties");
setActiveModule("builder");
}}
>
{selectedVersion?.version ?? "v 0.0.0"}
<div className="icon">
<HelpIcon />
</div>
@@ -83,8 +106,9 @@ const Footer: React.FC = () => {
{!isPlaying && (
<div
className={`shortcut-helper-overlay ${showShortcuts ? "visible" : ""
}`}
className={`shortcut-helper-overlay ${
showShortcuts ? "visible" : ""
}`}
>
<ShortcutHelper setShowShortcuts={setShowShortcuts} />
</div>

View File

@@ -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<LineProps>) {
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<LineProps>) {
});
}
}
gl.domElement.style.cursor = 'default';
handleCanvasCursors('default');
}
}
@@ -224,7 +225,7 @@ function Line({ points }: Readonly<LineProps>) {
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<LineProps>) {
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<LineProps>) {
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)
}}

View File

@@ -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<THREE.ShaderMaterial>(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<THREE.Vector3 | null>(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)
}}

View File

@@ -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 (

View File

@@ -45,3 +45,9 @@ $cursor-grabing: url("../../assets/cursors/close.svg") 8 8, default;
}
}
.scene-container.hand-closed {
canvas {
cursor: $cursor-grabing !important;
}
}

View File

@@ -3,14 +3,19 @@ export const handleCanvasCursors = (name: string) => {
if (!canvas) return;
const cursorMap: Record<string, string> = {
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
};

View File

@@ -0,0 +1,46 @@
const actionNotes: Record<string, string> = {
'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<string>();
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);
};
}