feat: Enhance cursor handling and mouse action notes in the footer and builder components
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
}}
|
||||
|
||||
@@ -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)
|
||||
}}
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -45,3 +45,9 @@ $cursor-grabing: url("../../assets/cursors/close.svg") 8 8, default;
|
||||
}
|
||||
}
|
||||
|
||||
.scene-container.hand-closed {
|
||||
canvas {
|
||||
cursor: $cursor-grabing !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
46
app/src/utils/mouseUtils/mouseHelper.ts
Normal file
46
app/src/utils/mouseUtils/mouseHelper.ts
Normal 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);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user