2025-04-10 12:16:11 +00:00
|
|
|
import React, { useEffect } from "react";
|
|
|
|
import useModuleStore, { useThreeDStore } from "../../store/useModuleStore";
|
|
|
|
import useToggleStore from "../../store/useUIToggleStore";
|
2025-04-10 12:22:30 +00:00
|
|
|
import {
|
|
|
|
useActiveSubTool,
|
|
|
|
useActiveTool,
|
|
|
|
useAddAction,
|
|
|
|
useDeleteTool,
|
|
|
|
useSelectedWallItem,
|
|
|
|
useToggleView,
|
|
|
|
useToolMode,
|
|
|
|
} from "../../store/store";
|
2025-04-10 12:16:11 +00:00
|
|
|
import { usePlayButtonStore } from "../../store/usePlayButtonStore";
|
2025-04-10 12:22:30 +00:00
|
|
|
import { detectModifierKeys } from "./detectModifierKeys";
|
2025-04-10 12:16:11 +00:00
|
|
|
|
2025-04-10 12:22:30 +00:00
|
|
|
const KeyPressListener: React.FC = () => {
|
2025-05-08 08:12:42 +00:00
|
|
|
const { activeModule, setActiveModule } = useModuleStore();
|
|
|
|
const { setActiveSubTool } = useActiveSubTool();
|
|
|
|
const { toggleUI, setToggleUI } = useToggleStore();
|
|
|
|
const { setToggleThreeD } = useThreeDStore();
|
|
|
|
const { setToolMode } = useToolMode();
|
|
|
|
const { setIsPlaying } = usePlayButtonStore();
|
|
|
|
const { toggleView, setToggleView } = useToggleView();
|
|
|
|
const { setDeleteTool } = useDeleteTool();
|
|
|
|
const { setAddAction } = useAddAction();
|
|
|
|
const { setSelectedWallItem } = useSelectedWallItem();
|
|
|
|
const { setActiveTool } = useActiveTool();
|
|
|
|
|
|
|
|
const isTextInput = (element: Element | null): boolean =>
|
|
|
|
element instanceof HTMLInputElement ||
|
|
|
|
element instanceof HTMLTextAreaElement ||
|
|
|
|
element?.getAttribute("contenteditable") === "true";
|
|
|
|
|
|
|
|
const handleModuleSwitch = (keyCombination: string) => {
|
|
|
|
const modules: Record<string, string> = {
|
|
|
|
"1": "builder",
|
|
|
|
"2": "simulation",
|
|
|
|
"3": "visualization",
|
|
|
|
"4": "market",
|
|
|
|
};
|
|
|
|
const module = modules[keyCombination];
|
|
|
|
if (module && !toggleView) {
|
|
|
|
setActiveTool("cursor");
|
|
|
|
setActiveSubTool("cursor");
|
|
|
|
if (module === "market") setToggleUI(false);
|
|
|
|
setActiveModule(module);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const handlePrimaryTools = (key: string) => {
|
|
|
|
const toolMap: Record<string, string> = {
|
|
|
|
V: "cursor",
|
|
|
|
X: "delete",
|
|
|
|
H: "free-hand",
|
|
|
|
};
|
|
|
|
const tool = toolMap[key];
|
|
|
|
if (tool) {
|
|
|
|
setActiveTool(tool);
|
|
|
|
setActiveSubTool(tool);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleBuilderShortcuts = (key: string) => {
|
|
|
|
if (activeModule !== "builder") return;
|
|
|
|
|
|
|
|
if (key === "TAB") {
|
|
|
|
const toggleTo2D = toggleView;
|
|
|
|
setToggleView(!toggleTo2D);
|
|
|
|
setToggleThreeD(toggleTo2D);
|
|
|
|
if (toggleTo2D) {
|
|
|
|
setSelectedWallItem(null);
|
|
|
|
setDeleteTool(false);
|
|
|
|
setAddAction(null);
|
2025-04-10 12:16:11 +00:00
|
|
|
}
|
2025-05-08 08:12:42 +00:00
|
|
|
setActiveTool("cursor");
|
|
|
|
setActiveSubTool("cursor");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// These should only apply in 2D view
|
|
|
|
const twoDToolConfigs: Record<string, { tool: string; mode: string }> = {
|
|
|
|
Q: { tool: "draw-wall", mode: "Wall" },
|
|
|
|
"6": { tool: "draw-wall", mode: "Wall" },
|
|
|
|
R: { tool: "draw-aisle", mode: "Aisle" },
|
|
|
|
"7": { tool: "draw-aisle", mode: "Aisle" },
|
|
|
|
E: { tool: "draw-zone", mode: "Zone" },
|
|
|
|
"8": { tool: "draw-zone", mode: "Zone" },
|
|
|
|
T: { tool: "draw-floor", mode: "Floor" },
|
|
|
|
"9": { tool: "draw-floor", mode: "Floor" },
|
|
|
|
};
|
2025-04-10 12:16:11 +00:00
|
|
|
|
2025-05-08 08:12:42 +00:00
|
|
|
const config = twoDToolConfigs[key];
|
|
|
|
if (toggleView && config) {
|
|
|
|
setActiveTool(config.tool);
|
|
|
|
setToolMode(config.mode);
|
|
|
|
}
|
2025-04-10 12:22:30 +00:00
|
|
|
|
2025-05-08 08:12:42 +00:00
|
|
|
// Measurement tool should work in both 2D and 3D
|
|
|
|
if (key === "M") {
|
|
|
|
setActiveTool("measure");
|
|
|
|
setToolMode("MeasurementScale");
|
|
|
|
}
|
|
|
|
};
|
2025-04-10 12:16:11 +00:00
|
|
|
|
|
|
|
|
2025-05-08 08:12:42 +00:00
|
|
|
const handleKeyPress = (event: KeyboardEvent) => {
|
|
|
|
if (isTextInput(document.activeElement)) return;
|
2025-04-10 12:16:11 +00:00
|
|
|
|
2025-05-08 08:12:42 +00:00
|
|
|
const keyCombination = detectModifierKeys(event);
|
|
|
|
if (!keyCombination || ["F5", "F11", "F12"].includes(event.key) || keyCombination === "Ctrl+R") return;
|
|
|
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
|
|
|
if (keyCombination === "Ctrl+\\") {
|
|
|
|
if (activeModule !== "market") setToggleUI(!toggleUI);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Active module selection (builder, simulation, etc.)
|
|
|
|
handleModuleSwitch(keyCombination);
|
|
|
|
// Common editing tools: cursor | delete | free-hand
|
|
|
|
handlePrimaryTools(keyCombination);
|
|
|
|
// Shortcuts specific to the builder module (e.g., drawing and measurement tools)
|
|
|
|
handleBuilderShortcuts(keyCombination);
|
|
|
|
|
|
|
|
// Shortcut to enter play mode
|
|
|
|
if (keyCombination === "Ctrl+P" && !toggleView) {
|
|
|
|
setIsPlaying(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (keyCombination === "ESCAPE") {
|
|
|
|
setActiveTool("cursor");
|
|
|
|
setIsPlaying(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Placeholder for future implementation
|
|
|
|
if (["Ctrl+Z", "Ctrl+Y", "Ctrl+Shift+Z", "Ctrl+H", "Ctrl+F", "Ctrl+?"].includes(keyCombination)) {
|
|
|
|
// Implement undo/redo/help/find/shortcuts
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
window.addEventListener("keydown", handleKeyPress);
|
|
|
|
return () => window.removeEventListener("keydown", handleKeyPress);
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
}, [activeModule, toggleUI, toggleView]);
|
2025-04-10 12:16:11 +00:00
|
|
|
|
2025-05-08 08:12:42 +00:00
|
|
|
return null;
|
2025-04-10 12:16:11 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
export default KeyPressListener;
|