diff --git a/app/package-lock.json b/app/package-lock.json index a820896..112c62c 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -2019,7 +2019,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "devOptional": true, + "dev": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -2031,7 +2031,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "devOptional": true, + "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -4134,26 +4134,6 @@ "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@testing-library/dom": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", - "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.3.0", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@testing-library/jest-dom": { "version": "5.17.0", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", @@ -4265,25 +4245,25 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "devOptional": true + "dev": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "devOptional": true + "dev": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "devOptional": true + "dev": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "devOptional": true + "dev": true }, "node_modules/@turf/along": { "version": "7.2.0", @@ -9017,7 +8997,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "devOptional": true + "dev": true }, "node_modules/cross-env": { "version": "7.0.3", @@ -9885,7 +9865,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.3.1" } @@ -15235,7 +15215,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "devOptional": true + "dev": true }, "node_modules/makeerror": { "version": "1.0.12", @@ -20694,7 +20674,7 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "devOptional": true, + "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -20737,7 +20717,7 @@ "version": "8.3.4", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "devOptional": true, + "dev": true, "dependencies": { "acorn": "^8.11.0" }, @@ -20749,7 +20729,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "devOptional": true + "dev": true }, "node_modules/tsconfig-paths": { "version": "3.15.0", @@ -21236,7 +21216,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "devOptional": true + "dev": true }, "node_modules/v8-to-istanbul": { "version": "8.1.1", @@ -22295,7 +22275,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "devOptional": true, + "dev": true, "engines": { "node": ">=6" } diff --git a/app/src/styles/components/input.scss b/app/src/styles/components/input.scss index f627c2c..a45e03b 100644 --- a/app/src/styles/components/input.scss +++ b/app/src/styles/components/input.scss @@ -7,14 +7,28 @@ input { width: 100%; padding: 2px 4px; border-radius: #{$border-radius-small}; - border: 1px solid var(--border-color); - outline: none; + outline: 2px solid var(--border-color); + outline-offset: -2px; + border: none; background: transparent; color: var(--input-text-color); &:focus, &:active { - border: 1px solid var(--accent-color); + outline: 1px solid var(--accent-color); + } + + &:-webkit-autofill, + &:-webkit-autofill:hover, + &:-webkit-autofill:focus, + &:-webkit-autofill:active { + // Text styles + -webkit-text-fill-color: var(--input-text-color) !important; + caret-color: var(--input-text-color); + + // Background styles + background-color: var(--background-color) !important; + -webkit-box-shadow: 0 0 0px 1000px var(--background-color) inset !important; } } @@ -615,6 +629,7 @@ input { input { border: none; + outline: none; background: transparent; &::placeholder { diff --git a/app/src/utils/shortcutkeys/detectModifierKeys.ts b/app/src/utils/shortcutkeys/detectModifierKeys.ts new file mode 100644 index 0000000..e5b7ded --- /dev/null +++ b/app/src/utils/shortcutkeys/detectModifierKeys.ts @@ -0,0 +1,30 @@ +// Function to detect if Shift, Ctrl, Alt, or combinations are pressed +// and return the corresponding key combination string +export const detectModifierKeys = (event: KeyboardEvent): string => { + const modifiers = [ + event.ctrlKey ? "Ctrl" : "", + event.altKey ? "Alt" : "", + event.shiftKey ? "Shift" : "", + event.metaKey ? "Meta" : "" // Add support for Command/Win key + ].filter(Boolean); + + // Ignore modifier keys when they're pressed alone + const isModifierKey = [ + "Control", "Shift", "Alt", "Meta", + "Ctrl", "AltGraph", "OS" // Additional modifier key aliases + ].includes(event.key); + + const mainKey = isModifierKey ? "" : event.key.toUpperCase(); + + // Handle special cases for keys with different representations + const normalizedKey = mainKey === " " ? "Space" : mainKey; + + // Build the combination string + if (modifiers.length > 0 && normalizedKey) { + return `${modifiers.join("+")}+${normalizedKey}`; + } else if (modifiers.length > 0) { + return modifiers.join("+"); + } else { + return normalizedKey; + } +}; diff --git a/app/src/utils/shortcutkeys/handleShortcutKeys.ts b/app/src/utils/shortcutkeys/handleShortcutKeys.ts index 57fdf66..e54b792 100644 --- a/app/src/utils/shortcutkeys/handleShortcutKeys.ts +++ b/app/src/utils/shortcutkeys/handleShortcutKeys.ts @@ -1,85 +1,70 @@ // Importing React and useEffect from React library import React, { useEffect } from "react"; -// Importing the necessary hooks and types from React and TypeScript + +// Importing the necessary hooks and types from the application's state management stores import useModuleStore, { useThreeDStore } from "../../store/useModuleStore"; import useToggleStore from "../../store/useUIToggleStore"; -import { useActiveSubTool, useActiveTool, useAddAction, useDeleteTool, useSelectedWallItem, useToggleView, useToolMode } from "../../store/store"; +import { + useActiveSubTool, + useActiveTool, + useAddAction, + useDeleteTool, + useSelectedWallItem, + useToggleView, + useToolMode, +} from "../../store/store"; import { usePlayButtonStore } from "../../store/usePlayButtonStore"; +// Utility function to detect modifier keys (Ctrl, Alt, Shift) and key combinations +import { detectModifierKeys } from "./detectModifierKeys"; + +// KeyPressListener component to handle global keyboard shortcuts const KeyPressListener: React.FC = () => { - // Function to detect if Shift, Ctrl, Alt, or combinations are pressed - const detectModifierKeys = (event: KeyboardEvent): string => { - const modifiers = [ - event.ctrlKey ? "Ctrl" : "", - event.altKey ? "Alt" : "", - event.shiftKey ? "Shift" : "", - event.metaKey ? "Meta" : "" // Add support for Command/Win key - ].filter(Boolean); + // Accessing state and actions from different stores + const { activeModule, setActiveModule } = useModuleStore(); // Module management (e.g., builder, simulation, visualization) + const { setActiveSubTool } = useActiveSubTool(); // Sub-tool management + const { toggleUI, setToggleUI } = useToggleStore(); // UI visibility toggle + const { setToggleThreeD } = useThreeDStore(); // 3D view toggle + const { setToolMode } = useToolMode(); // Tool mode management + const { setIsPlaying } = usePlayButtonStore(); // Play button state management - // Ignore modifier keys when they're pressed alone - const isModifierKey = [ - "Control", "Shift", "Alt", "Meta", - "Ctrl", "AltGraph", "OS" // Additional modifier key aliases - ].includes(event.key); - - const mainKey = isModifierKey ? "" : event.key.toUpperCase(); - - // Handle special cases for keys with different representations - const normalizedKey = mainKey === " " ? "Space" : mainKey; - - // Build the combination string - if (modifiers.length > 0 && normalizedKey) { - return `${modifiers.join("+")}+${normalizedKey}`; - } else if (modifiers.length > 0) { - return modifiers.join("+"); - } else { - return normalizedKey; - } - }; - - // Importing the necessary hooks from the store - const { activeModule, setActiveModule } = useModuleStore(); - const { setActiveSubTool } = useActiveSubTool(); - const { toggleUI, setToggleUI } = useToggleStore(); - const { setToggleThreeD } = useThreeDStore(); - const { setToolMode } = useToolMode(); - const { setIsPlaying } = usePlayButtonStore(); - - // wall options - const { toggleView, setToggleView } = useToggleView(); - const { setDeleteTool } = useDeleteTool(); - const { setAddAction } = useAddAction(); - const { setSelectedWallItem } = useSelectedWallItem(); - const { setActiveTool } = useActiveTool(); + // Wall and tool-related actions + const { toggleView, setToggleView } = useToggleView(); // 2D/3D toggle state + const { setDeleteTool } = useDeleteTool(); // Delete tool action + const { setAddAction } = useAddAction(); // Add action management + const { setSelectedWallItem } = useSelectedWallItem(); // Selected wall item management + const { setActiveTool } = useActiveTool(); // Active tool management + // useEffect to manage global keyboard shortcuts useEffect(() => { // Function to handle keydown events const handleKeyPress = (event: KeyboardEvent) => { - + // Identify the currently focused element const activeElement = document.activeElement; - const isTyping = - activeElement instanceof HTMLInputElement || - activeElement instanceof HTMLTextAreaElement || - (activeElement && activeElement.getAttribute('contenteditable') === 'true'); - + // Check if the user is typing in an input field, textarea, or contenteditable element + const isTyping = + activeElement instanceof HTMLInputElement || + activeElement instanceof HTMLTextAreaElement || + (activeElement && activeElement.getAttribute("contenteditable") === "true"); + if (isTyping) { - return; // Don't trigger shortcuts while typing + return; // Skip shortcut handling while typing } + // Detect the combination of keys pressed const keyCombination = detectModifierKeys(event); - - // Allow default behavior for F5 and F12 + + // Allow browser default behavior for specific keys (e.g., F5 for refresh) if (["F5", "F11", "F12"].includes(event.key) || keyCombination === "Ctrl+R") { return; } - // Prevent default action for the key press + // Prevent the default browser action for other handled key presses event.preventDefault(); - // Detect the key combination pressed if (keyCombination) { - // Check for specific key combinations to switch modules + // Switch between different modules (e.g., builder, simulation) if (keyCombination === "1" && !toggleView) { setActiveTool("cursor"); setActiveSubTool("cursor"); @@ -102,12 +87,12 @@ const KeyPressListener: React.FC = () => { setActiveModule("market"); } - // sidebar toggle + // Toggle UI visibility if (keyCombination === "Ctrl+." && activeModule !== "market") { setToggleUI(!toggleUI); } - // tools toggle + // Tool selection shortcuts if (keyCombination === "V") { setActiveTool("cursor"); setActiveSubTool("cursor"); @@ -121,14 +106,15 @@ const KeyPressListener: React.FC = () => { setActiveSubTool("free-hand"); } - // player toggle + // Toggle play mode if (keyCombination === "Ctrl+P" && !toggleView) { setIsPlaying(true); } - // builder key combination + // Builder-specific shortcuts if (activeModule === "builder") { if (keyCombination === "TAB") { + // Switch between 2D and 3D views if (toggleView) { setToggleView(false); setToggleThreeD(true); @@ -142,7 +128,8 @@ const KeyPressListener: React.FC = () => { setActiveTool("cursor"); } } - // builder tools + + // Wall-related tools if (toggleView) { if (keyCombination === "Q" || keyCombination === "6") { setActiveTool("draw-wall"); @@ -161,21 +148,34 @@ const KeyPressListener: React.FC = () => { setToolMode("Floor"); } } + + // Measurement tool if (keyCombination === "M") { setActiveTool("measure"); setToolMode("MeasurementScale"); } } - // Undo redo + // Undo and redo actions if (keyCombination === "Ctrl+Z") { - // Handle undo action here + // Handle undo action } if (keyCombination === "Ctrl+Y" || keyCombination === "Ctrl+Shift+Z") { - // Handle redo action here + // Handle redo action } - // cleanup function to remove event listener + // Helper actions + if (keyCombination === "Ctrl+H") { + // Open help + } + if (keyCombination === "Ctrl+F") { + // Open find functionality + } + if (keyCombination === "Ctrl+?") { + // Show shortcuts info + } + + // Reset to cursor tool and stop play mode if (keyCombination === "ESCAPE") { setActiveTool("cursor"); setIsPlaying(false); @@ -183,16 +183,16 @@ const KeyPressListener: React.FC = () => { } }; - // Add event listener for keydown + // Add keydown event listener window.addEventListener("keydown", handleKeyPress); - // Cleanup function to remove event listener + // Cleanup function to remove the event listener return () => { window.removeEventListener("keydown", handleKeyPress); }; - }, [activeModule, toggleUI, toggleView]); // Empty dependency array ensures this runs only once + }, [activeModule, toggleUI, toggleView]); // Dependencies to reapply effect if these values change - return null; // No UI component to render, so return null + return null; // This component does not render any UI }; export default KeyPressListener;