Merge branch 'main' into simulation-animation
|
@ -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.
|
||||
|
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 480 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 1.2 MiB |
|
@ -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;
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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<THREE.Group>; }) {
|
||||
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"));
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
|
@ -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;
|
||||
|
|