diff --git a/app/docs/documents/projectStructure.md b/app/docs/documents/projectStructure.md index 1e12346..234dabb 100644 --- a/app/docs/documents/projectStructure.md +++ b/app/docs/documents/projectStructure.md @@ -1,10 +1,13 @@ -# Project Folder Structure +# **Project Folder Structure Maintenance** -This document provides a detailed description of the purpose of each folder in the project by root level, along with the folder hierarchy. +This document outlines the project’s folder structure, describing the purpose of each folder and file. It also includes guidelines for development, collaboration, and scalability. -## Folder Hierarchy +--- + +## **Folder Hierarchy** ``` +πŸ“ .github πŸ“ src β”œβ”€β”€ πŸ“ assets β”œβ”€β”€ πŸ“ components @@ -14,94 +17,195 @@ This document provides a detailed description of the purpose of each folder in t β”‚ β”œβ”€β”€ πŸ“ builder β”‚ β”œβ”€β”€ πŸ“ simulation β”‚ └── πŸ“ visualization +β”œβ”€β”€ πŸ“ pages β”œβ”€β”€ πŸ“ services β”œβ”€β”€ πŸ“ store β”œβ”€β”€ πŸ“ styles β”œβ”€β”€ πŸ“ tests β”œβ”€β”€ πŸ“ types β”œβ”€β”€ πŸ“ utils +β”œβ”€β”€ πŸ“ temp β”œβ”€β”€ App.css β”œβ”€β”€ App.tsx β”œβ”€β”€ index.css β”œβ”€β”€ main.tsx -└── vite-env.d.ts +β”œβ”€β”€ Dockerfile +└── nginx.conf ``` --- -## Description of Each Folder +## **Folder and File Descriptions** -### πŸ“ `src` -The root directory for all source code related to the application. - -#### πŸ“ `assets` -- **Purpose:** Contains static assets such as images, icons, fonts, or other resources. -- **Example:** - - `react.svg`: A static SVG file used in the project. - -#### πŸ“ `components` -- **Purpose:** Contains reusable React components, serving as building blocks for the user interface. -- **Example:** - - Buttons, modals, headers, and forms. - -#### πŸ“ `functions` -- **Purpose:** Stores pure functions or logic that can be reused across the application. -- **Example:** - - Utility functions for data transformation or computation. - -#### πŸ“ `hooks` -- **Purpose:** Holds custom React hooks for managing specific logic or behaviors. -- **Example:** - - Hooks for state management, API calls, or reusable effects. - -#### πŸ“ `modules` -- **Purpose:** Organizes high-level, feature-specific code into subdirectories. - - **πŸ“ builder:** Manages functionalities and components related to building or configuring features. - - **πŸ“ simulation:** Handles processes or logic related to simulations or dynamic scenarios. - - **πŸ“ visualization:** Focuses on displaying data visually, such as charts, graphs, or interactive UI elements. - -#### πŸ“ `services` -- **Purpose:** Encapsulates external service interactions, such as API calls or library integrations. -- **Example:** - - REST API clients or authentication handlers. - -#### πŸ“ `store` -- **Purpose:** Contains state management logic and configurations for tools like Redux or Zustand. -- **Example:** - - Redux slices, context providers, or global state stores. - -#### πŸ“ `styles` -- **Purpose:** Includes global CSS, SCSS, or theming resources. -- **Example:** - - Global styles, theme variables, or resets. - -#### πŸ“ `tests` -- **Purpose:** Stores test files for unit testing, integration testing, and mock data. -- **Example:** - - Test files (`*.test.tsx`, `*.spec.ts`) or mock utilities. - -#### πŸ“ `types` -- **Purpose:** Contains shared TypeScript type definitions and interfaces. -- **Example:** - - Type declarations for props, data models, or API responses. - -#### πŸ“ `utils` -- **Purpose:** Includes general-purpose utility files that don’t fit into other specific folders. -- **Example:** - - Helper functions like debouncers or date formatters. +### πŸ“ **`src`** +The root directory for all application source code. --- -## Root-Level Files +### πŸ“ **`assets`** +- **Purpose:** Static assets like images, fonts, and icons. +- **Examples:** + - Subfolders for `images`, `icons`, and `fonts`. + - Use descriptive file names (e.g., `logo.svg`, `Roboto-Regular.ttf`). -### `App.tsx` -- **Purpose:** The root React component, initializing the main application layout and logic. +--- -### `index.css` -- **Purpose:** Contains global styles applied throughout the application. +### πŸ“ **`components`** +- **Purpose:** Reusable React components forming the building blocks of the UI. +- **Examples:** + - Buttons, modals, input fields, and headers. + - Organize larger components into folders (e.g., `Button/`). -### `main.tsx` -- **Purpose:** The entry point of the app, rendering the React application and setting up the React DOM. +--- -### `vite-env.d.ts` -- **Purpose:** TypeScript environment configuration file for Vite. +### πŸ“ **`functions`** +- **Purpose:** Pure functions and reusable logic. +- **Examples:** + - `formatDate.ts`: Format dates into readable strings. + - `calculateTotal.ts`: Logic for cart total calculation. + +--- + +### πŸ“ **`hooks`** +- **Purpose:** Custom React hooks encapsulating reusable logic. +- **Examples:** + - `useAuth.ts`: Manages authentication logic. + - `useFetch.ts`: Fetches data from APIs. + +--- + +### πŸ“ **`modules`** +- **Purpose:** Feature-specific code organized into subfolders. +- **Subfolders:** + - **`builder`**: For UI components and logic related to building features. + - **`simulation`**: Code for running simulations. + - **`visualization`**: Visualization components like charts and graphs. + +--- + +### πŸ“ **`pages`** +- **Purpose:** Pages that represent routes in the application. +- **Examples:** + - `HomePage.tsx`: Home route. + - `DashboardPage.tsx`: Dashboard route. + +--- + +### πŸ“ **`services`** +- **Purpose:** External API interactions and third-party service logic. +- **Examples:** + - `apiClient.ts`: Wrapper for HTTP requests. + - `authService.ts`: Authentication services. + +--- + +### πŸ“ **`store`** +- **Purpose:** State management logic using tools like Redux or Context API. +- **Examples:** + - `authSlice.ts`: Authentication-related state management. + - `cartSlice.ts`: Shopping cart state management. + +--- + +### πŸ“ **`styles`** +- **Purpose:** Global and modular styles. +- **Examples:** + - `global.css`: Global styles. + - `theme.scss`: Theme variables. + +--- + +### πŸ“ **`tests`** +- **Purpose:** Test files for unit, integration, and E2E testing. +- **Examples:** + - `Button.test.tsx`: Tests for the `Button` component. + - `mockData.ts`: Mock API responses. + +--- + +### πŸ“ **`types`** +- **Purpose:** Shared TypeScript type definitions and interfaces. +- **Examples:** + - `User.ts`: User-related type definitions. + - `ApiResponse.ts`: API response types. + +--- + +### πŸ“ **`utils`** +- **Purpose:** General-purpose utility functions and constants. +- **Examples:** + - `debounce.ts`: Debounce function. + - `constants.ts`: Shared constants. + +--- + +### πŸ“ **`temp`** +- **Purpose:** A temporary directory for experimental or work-in-progress code. +- **Guidelines:** + - This folder is included in `.gitignore` to prevent its contents from affecting the main project. + - Move any experimental work here to isolate it from production-ready code. + +--- + +### **Root-Level Files** + +- **`App.tsx`**: Root React component. +- **`main.tsx`**: Entry point for the app. +- **`Dockerfile`**: Docker configuration for containerizing the application. +- **`nginx.conf`**: Configuration for the Nginx server. + +--- + +## **Development Guidelines** + +### **Environment Management** +- **`.env.local`**: Use this file for testing and development-specific variables. +- **`.env`**: Should only contain variables required for deployed services (e.g., API base URLs). + +### **Collaboration Best Practices** +1. **Use the `temp` Folder for Experiments:** + Any experimental work should be isolated in the `temp` folder. Avoid committing temporary code to the main repository. + +2. **Documentation:** + - Read the shared documentation in the `docify` and `.github` folders before starting work. + - Follow any guidelines or standards outlined in these documents. + +3. **Branching Rules:** + - Do not merge other branches into your branch without proper code review or necessity. + - Use meaningful branch names (e.g., `feature/auth-module`, `fix/header-bug`). + +4. **Code Reviews:** + - Ensure all code undergoes peer review before merging. + - Use PR templates provided in the `.github` folder to document changes. + +--- + +## **Additional Instructions for Large-Scale Projects** + +1. **Folder Depth:** + - Avoid excessive nesting of folders to maintain simplicity. + - Refactor large folders into modules or domains as the project scales. + +2. **Dependency Management:** + - Regularly review and update dependencies. + - Remove unused or deprecated libraries to reduce technical debt. + +3. **Automate Workflows:** + - Use CI/CD pipelines to automate testing, building, and deployment processes. + - Integrate tools like Prettier, ESLint, and Husky to enforce code quality. + +4. **Versioning and Change Logs:** + - Maintain a changelog to track major updates. + - Use semantic versioning (`x.y.z`) for releases. + +5. **Performance Monitoring:** + - Regularly monitor and optimize app performance. + - Use tools like Lighthouse, React DevTools, and browser profilers. + +6. **Error Handling:** + - Centralize error handling using utilities or middleware. + - Log errors in both the frontend and backend for debugging. + +7. **Documentation:** + - Continuously update this document to reflect structural or procedural changes. + - Ensure all team members are familiar with the documentation. diff --git a/app/src/assets/image/feneration.png b/app/src/assets/image/categories/feneration.png similarity index 100% rename from app/src/assets/image/feneration.png rename to app/src/assets/image/categories/feneration.png diff --git a/app/src/assets/image/categories/machines.png b/app/src/assets/image/categories/machines.png new file mode 100644 index 0000000..a441d48 Binary files /dev/null and b/app/src/assets/image/categories/machines.png differ diff --git a/app/src/assets/image/vehicles.png b/app/src/assets/image/categories/vehicles.png similarity index 100% rename from app/src/assets/image/vehicles.png rename to app/src/assets/image/categories/vehicles.png diff --git a/app/src/assets/image/categories/workStation.png b/app/src/assets/image/categories/workStation.png new file mode 100644 index 0000000..43221ae Binary files /dev/null and b/app/src/assets/image/categories/workStation.png differ diff --git a/app/src/assets/image/categories/worker.png b/app/src/assets/image/categories/worker.png new file mode 100644 index 0000000..e2287e7 Binary files /dev/null and b/app/src/assets/image/categories/worker.png differ diff --git a/app/src/assets/image/machines.png b/app/src/assets/image/machines.png deleted file mode 100644 index 199870d..0000000 Binary files a/app/src/assets/image/machines.png and /dev/null differ diff --git a/app/src/assets/image/userImage.png b/app/src/assets/image/userImage.png deleted file mode 100644 index 51af26c..0000000 Binary files a/app/src/assets/image/userImage.png and /dev/null differ diff --git a/app/src/assets/image/workStation.png b/app/src/assets/image/workStation.png deleted file mode 100644 index 6345487..0000000 Binary files a/app/src/assets/image/workStation.png and /dev/null differ diff --git a/app/src/assets/image/worker.png b/app/src/assets/image/worker.png deleted file mode 100644 index 7067644..0000000 Binary files a/app/src/assets/image/worker.png and /dev/null differ diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index 67bf969..9e04615 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -1,16 +1,20 @@ import React, { useEffect, useMemo, useState } from "react"; import Search from "../../ui/inputs/Search"; -import vehicle from "../../../assets/image/vehicles.png"; -import workStation from "../../../assets/image/workStation.png"; -import machines from "../../../assets/image/machines.png"; -import feneration from "../../../assets/image/feneration.png"; -import worker from "../../../assets/image/worker.png"; import { getCategoryAsset } from "../../../services/factoryBuilder/assest/assets/getCategoryAsset"; import arch from "../../../assets/gltf-glb/arch.glb"; import door from "../../../assets/gltf-glb/door.glb"; import window from "../../../assets/gltf-glb/window.glb"; import { fetchAssets } from "../../../services/marketplace/fetchAssets"; import { useSelectedItem } from "../../../store/store"; + +// images ------------------- +import vehicle from "../../../assets/image/categories/vehicles.png"; +import workStation from "../../../assets/image/categories/workStation.png"; +import machines from "../../../assets/image/categories/machines.png"; +import feneration from "../../../assets/image/categories/feneration.png"; +import worker from "../../../assets/image/categories/worker.png"; +// ------------------------------------- + interface AssetProp { filename: string; thumbnail?: string; diff --git a/app/src/components/ui/Tools.tsx b/app/src/components/ui/Tools.tsx index 8fa95a9..5938aa3 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -115,17 +115,10 @@ const Tools: React.FC = () => { setOpenDrop(false); // Close the dropdown } }; - const handleEscKeyPress = (event: KeyboardEvent) => { - if (event.key === "Escape") { - setIsPlaying(false); // Set isPlaying to false when Escape key is pressed - } - }; document.addEventListener("mousedown", handleOutsideClick); - document.addEventListener("keydown", handleEscKeyPress); // Listen for ESC key return () => { document.removeEventListener("mousedown", handleOutsideClick); - document.removeEventListener("keydown", handleEscKeyPress); // Clean up the event listener }; }, []); useEffect(() => { diff --git a/app/src/modules/scene/camera/camMode.tsx b/app/src/modules/scene/camera/camMode.tsx index ba1aa0f..b4a59b0 100644 --- a/app/src/modules/scene/camera/camMode.tsx +++ b/app/src/modules/scene/camera/camMode.tsx @@ -5,6 +5,7 @@ import { useCamMode, useToggleView } from '../../../store/store'; import { useKeyboardControls } from '@react-three/drei'; import switchToThirdPerson from './switchToThirdPerson'; import switchToFirstPerson from './switchToFirstPerson'; +import { detectModifierKeys } from '../../../utils/shortcutkeys/detectModifierKeys'; const CamMode: React.FC = () => { const { camMode, setCamMode } = useCamMode(); @@ -37,7 +38,9 @@ const CamMode: React.FC = () => { const handleKeyPress = async (event: any) => { if (!state.controls) return; - if (event.key === "/" && !isTransitioning && !toggleView) { + const keyCombination = detectModifierKeys(event); + + if (keyCombination === "/" && !isTransitioning && !toggleView) { setIsTransitioning(true); state.controls.mouseButtons.left = CONSTANTS.controlsTransition.leftMouse; state.controls.mouseButtons.right = CONSTANTS.controlsTransition.rightMouse; @@ -81,9 +84,7 @@ const CamMode: React.FC = () => { } }); - return ( - <> - ); + return null; // This component does not render any UI }; export default CamMode; \ No newline at end of file diff --git a/app/src/modules/scene/controls/selection/copyPasteControls.tsx b/app/src/modules/scene/controls/selection/copyPasteControls.tsx index aed1325..5c07649 100644 --- a/app/src/modules/scene/controls/selection/copyPasteControls.tsx +++ b/app/src/modules/scene/controls/selection/copyPasteControls.tsx @@ -5,6 +5,7 @@ import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore, import { toast } from "react-toastify"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; +import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, selectionGroup, setDuplicatedObjects, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => { const { camera, controls, gl, scene, pointer, raycaster } = useThree(); @@ -38,10 +39,12 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas }; const onKeyDown = (event: KeyboardEvent) => { - if (event.ctrlKey && event.key.toLowerCase() === "c" && movedObjects.length === 0 && rotatedObjects.length === 0) { + const keyCombination = detectModifierKeys(event); + + if (keyCombination === "Ctrl+C" && movedObjects.length === 0 && rotatedObjects.length === 0) { copySelection(); } - if (event.ctrlKey && event.key.toLowerCase() === "v" && copiedObjects.length > 0 && pastedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { + if (keyCombination === "Ctrl+V" && copiedObjects.length > 0 && pastedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { pasteCopiedObjects(); } }; @@ -570,9 +573,7 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas setSelectedAssets([]); } - return ( - <> - ); + return null; // No visible output, but the component handles copy-paste functionality }; export default CopyPasteControls; \ No newline at end of file diff --git a/app/src/modules/scene/controls/selection/duplicationControls.tsx b/app/src/modules/scene/controls/selection/duplicationControls.tsx index 9d40a72..a127a30 100644 --- a/app/src/modules/scene/controls/selection/duplicationControls.tsx +++ b/app/src/modules/scene/controls/selection/duplicationControls.tsx @@ -6,6 +6,7 @@ import { toast } from "react-toastify"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; +import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedObjects, setpastedObjects, selectionGroup, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => { const { camera, controls, gl, scene, pointer, raycaster } = useThree(); @@ -39,12 +40,11 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb }; const onKeyDown = (event: KeyboardEvent) => { - if (event.key.toLowerCase() === "d") { - event.preventDefault(); - if (event.ctrlKey && event.key.toLowerCase() === "d" && selectedAssets.length > 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { + const keyCombination = detectModifierKeys(event); + + if (keyCombination === "Ctrl+D" && selectedAssets.length > 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { duplicateSelection(); } - } }; if (!toggleView) { @@ -552,9 +552,7 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb setSelectedAssets([]); } - return ( - <> - ); + return null; // This component does not render any UI }; export default DuplicationControls; \ No newline at end of file diff --git a/app/src/modules/scene/controls/selection/moveControls.tsx b/app/src/modules/scene/controls/selection/moveControls.tsx index 2a11c53..47a3747 100644 --- a/app/src/modules/scene/controls/selection/moveControls.tsx +++ b/app/src/modules/scene/controls/selection/moveControls.tsx @@ -5,6 +5,7 @@ import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore, // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { toast } from "react-toastify"; import * as Types from "../../../../types/world/worldTypes"; +import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) { const { camera, controls, gl, scene, pointer, raycaster } = useThree(); @@ -56,14 +57,16 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje }; const onKeyDown = (event: KeyboardEvent) => { + const keyCombination = detectModifierKeys(event); + if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || rotatedObjects.length > 0) return; - if (event.key.toLowerCase() === "g") { + if (keyCombination === "G") { if (selectedAssets.length > 0) { moveAssets(); itemsData.current = floorItems.filter((item: { modeluuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modeluuid)); } } - if (event.key.toLowerCase() === "escape") { + if (keyCombination === "ESCAPE") { event.preventDefault(); clearSelection(); @@ -453,9 +456,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje setSelectedAssets([]); } - return ( - <> - ) + return null; // No need to return anything, as this component is used for its side effects } export default MoveControls \ No newline at end of file diff --git a/app/src/modules/scene/controls/selection/rotateControls.tsx b/app/src/modules/scene/controls/selection/rotateControls.tsx index 90143ca..bef64a2 100644 --- a/app/src/modules/scene/controls/selection/rotateControls.tsx +++ b/app/src/modules/scene/controls/selection/rotateControls.tsx @@ -457,9 +457,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo setSelectedAssets([]); } - return ( - <> - ) + return null; // No need to return anything, as this component is used for its side effects } export default RotateControls \ No newline at end of file diff --git a/app/src/modules/scene/controls/selection/selectionControls.tsx b/app/src/modules/scene/controls/selection/selectionControls.tsx index 11c2dfb..acaed3a 100644 --- a/app/src/modules/scene/controls/selection/selectionControls.tsx +++ b/app/src/modules/scene/controls/selection/selectionControls.tsx @@ -14,6 +14,7 @@ import CopyPasteControls from "./copyPasteControls"; import MoveControls from "./moveControls"; import RotateControls from "./rotateControls"; import useModuleStore from "../../../../store/useModuleStore"; +import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; const SelectionControls: React.FC = () => { const { camera, controls, gl, scene, pointer } = useThree(); @@ -101,12 +102,13 @@ const SelectionControls: React.FC = () => { }; const onKeyDown = (event: KeyboardEvent) => { + const keyCombination = detectModifierKeys(event); if (movedObjects.length > 0 || rotatedObjects.length > 0) return; - if (event.key.toLowerCase() === "escape") { + if (keyCombination === "ESCAPE") { event.preventDefault(); clearSelection(); } - if (event.key.toLowerCase() === "delete") { + if (keyCombination === "DELETE") { event.preventDefault(); deleteSelection(); } diff --git a/app/src/modules/simulation/path/pathCreation.tsx b/app/src/modules/simulation/path/pathCreation.tsx index 3e13d27..345fff3 100644 --- a/app/src/modules/simulation/path/pathCreation.tsx +++ b/app/src/modules/simulation/path/pathCreation.tsx @@ -16,6 +16,7 @@ import { useFrame, useThree } from "@react-three/fiber"; import { useSubModuleStore } from "../../../store/useModuleStore"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; import { setEventApi } from "../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; +import { detectModifierKeys } from "../../../utils/shortcutkeys/detectModifierKeys"; function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObject; }) { const { isPlaying } = usePlayButtonStore(); @@ -40,11 +41,12 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec useEffect(() => { setTransformMode(null); const handleKeyDown = (e: KeyboardEvent) => { + const keyCombination = detectModifierKeys(e); if (!selectedActionSphere) return; - if (e.key === "g") { + if (keyCombination === "G") { setTransformMode((prev) => (prev === "translate" ? null : "translate")); } - if (e.key === "r") { + if (keyCombination === "R") { setTransformMode((prev) => (prev === "rotate" ? null : "rotate")); } }; diff --git a/app/src/styles/abstracts/mixins.scss b/app/src/styles/abstracts/mixins.scss index e3c57a2..5090d5b 100644 --- a/app/src/styles/abstracts/mixins.scss +++ b/app/src/styles/abstracts/mixins.scss @@ -9,3 +9,25 @@ justify-content: space-between; align-items: center; } + +// Array of base colors +$colors: ( + #f5550b, + #1bac1b, + #0099ff, + #d4c927, + #8400ff, + #13e9b3, + #df1dcf +); + +@mixin gradient-by-child($index) { + // Get the color based on the index passed + $base-color: nth($colors, $index); + // Apply gradient using the same color with different alpha values + background: linear-gradient( + 144.19deg, + rgba($base-color, 0.2) 16.62%, // 20% opacity + rgba($base-color, 0.08) 85.81% // 80% opacity + ); +} 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/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index 3b14aac..6b35de2 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -78,19 +78,19 @@ min-height: 50vh; max-height: 60vh; } - + .template-item { border: 1px solid #e0e0e0; border-radius: 8px; padding: 1rem; transition: box-shadow 0.3s ease; } - + .template-image-container { position: relative; padding-bottom: 56.25%; // 16:9 aspect ratio } - + .template-image { position: absolute; width: 100%; @@ -100,19 +100,19 @@ cursor: pointer; transition: transform 0.3s ease; } - + .template-details { display: flex; justify-content: space-between; align-items: center; margin-top: 0.5rem; } - + .template-name { cursor: pointer; font-weight: 500; } - + .delete-button { padding: 0.25rem 0.5rem; background: #ff4444; @@ -122,14 +122,13 @@ cursor: pointer; transition: opacity 0.3s ease; } - + .no-templates { text-align: center; color: #666; padding: 2rem; grid-column: 1 / -1; } - .widget-left-sideBar { min-height: 50vh; @@ -538,7 +537,6 @@ .floating { width: 100%; - } } @@ -1070,24 +1068,62 @@ .category-name { position: relative; z-index: 3; - font-size: var(--font-size-large); + font-size: var(--font-size-regular); // -webkit-text-fill-color: transparent; // -webkit-text-stroke: 1px black; } &::after { content: ""; - width: 50px; - height: 50px; + width: 60px; + height: 60px; border-radius: 50%; background-color: var(--circle-color, #000); position: absolute; - top: 50%; + top: 60%; right: -10px; transform: translate(0, -50%); - background: linear-gradient(144.19deg, - #f1e7cd 16.62%, - #fffaef 85.81%); + } + &:nth-child(1), &:nth-child(9) { + &::after { + @include gradient-by-child(1); // First child uses the first color + } + } + + &:nth-child(2), &:nth-child(10) { + &::after { + @include gradient-by-child(2); // Second child uses the second color + } + } + + &:nth-child(3), &:nth-child(11) { + &::after { + @include gradient-by-child(3); // Third child uses the third color + } + } + + &:nth-child(4), &:nth-child(12) { + &::after { + @include gradient-by-child(4); // Fourth child uses the fourth color + } + } + + &:nth-child(5), &:nth-child(13) { + &::after { + @include gradient-by-child(5); // Fifth child uses the fifth color + } + } + + &:nth-child(6), &:nth-child(14) { + &::after { + @include gradient-by-child(6); // Fifth child uses the fifth color + } + } + + &:nth-child(7), &:nth-child(15) { + &::after { + @include gradient-by-child(7); // Fifth child uses the fifth color + } } .category-image { @@ -1114,30 +1150,47 @@ .assets { width: 117px; height: 95px; - border-radius: 3.59px; + border-radius: #{$border-radius-small}; background-color: var(--background-color-gray); - padding: 8px; - padding-top: 12px; font-weight: $medium-weight; position: relative; overflow: hidden; + padding: 0; + &:hover { + .asset-name { + opacity: 1; + } + } .asset-name { - position: relative; + position: absolute; + top: 0; z-index: 3; + padding: 8px; + width: 100%; font-size: var(--font-size-regular); + background: color-mix( + in srgb, + var(--background-color) 40%, + transparent + ); + backdrop-filter: blur(5px); + opacity: 0; + transition: opacity 0.3s ease; + + /* Added properties for ellipsis */ + display: -webkit-box; /* Necessary for multiline truncation */ + -webkit-line-clamp: 2; /* Number of lines to show */ + -webkit-box-orient: vertical; /* Box orientation for the ellipsis */ + overflow: hidden; /* Hide overflowing content */ + text-overflow: ellipsis; /* Add ellipsis for truncated content */ } .asset-image { height: 100%; width: 100%; - position: absolute; - // top: 50%; - // right: 5px; - // transform: translate(0, -50%); - top: 0; - left: 0; z-index: 2; + object-fit: cover; } } } @@ -1200,4 +1253,4 @@ .assets-wrapper { margin: 0; } -} \ No newline at end of file +} diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index 19cd2b4..a30d9f3 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -765,20 +765,19 @@ .editWidgetOptions { position: absolute; - // top: 50%; - // left: 50%; - // transform: translate(-50%, -50%); background-color: var(--background-color); z-index: 3; display: flex; flex-direction: column; border-radius: 6px; overflow: hidden; + padding: 4px; min-width: 150px; .option { - padding: 8px 10px; + padding: 4px 10px; + border-radius: #{$border-radius-small}; color: var(--text-color); cursor: pointer; @@ -791,7 +790,8 @@ color: #f65648; &:hover { - background-color: #ffe3e0; + background-color: #f65648; + color: white; } } } 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 3dab047..43ca553 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;