Add context menu and context controls for asset manipulation
This commit is contained in:
@@ -3,6 +3,7 @@ import {
|
||||
useLoadingProgress,
|
||||
useRenameModeStore,
|
||||
useSaveVersion,
|
||||
useSelectedAssets,
|
||||
useSelectedComment,
|
||||
useSelectedFloorItem,
|
||||
useSocketStore,
|
||||
@@ -58,6 +59,7 @@ function MainScene() {
|
||||
const { setFloatingWidget } = useFloatingWidget();
|
||||
const { clearComparisonProduct } = useComparisonProduct();
|
||||
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
|
||||
const { selectedAssets } = useSelectedAssets();
|
||||
const { assetStore, productStore } = useSceneContext();
|
||||
const { products } = productStore();
|
||||
const { setName } = assetStore();
|
||||
@@ -97,18 +99,35 @@ function MainScene() {
|
||||
|
||||
const handleObjectRename = async (newName: string) => {
|
||||
if (!projectId) return
|
||||
let response = await setAssetsApi({
|
||||
modelUuid: selectedFloorItem.userData.modelUuid,
|
||||
modelName: newName,
|
||||
projectId
|
||||
});
|
||||
selectedFloorItem.userData = {
|
||||
...selectedFloorItem.userData,
|
||||
modelName: newName
|
||||
};
|
||||
setSelectedFloorItem(selectedFloorItem);
|
||||
setIsRenameMode(false);
|
||||
setName(selectedFloorItem.userData.modelUuid, response.modelName);
|
||||
if (selectedFloorItem) {
|
||||
setAssetsApi({
|
||||
modelUuid: selectedFloorItem.userData.modelUuid,
|
||||
modelName: newName,
|
||||
projectId
|
||||
}).then(() => {
|
||||
selectedFloorItem.userData = {
|
||||
...selectedFloorItem.userData,
|
||||
modelName: newName
|
||||
};
|
||||
setSelectedFloorItem(selectedFloorItem);
|
||||
setIsRenameMode(false);
|
||||
setName(selectedFloorItem.userData.modelUuid, newName);
|
||||
})
|
||||
} else if (selectedAssets.length === 1) {
|
||||
setAssetsApi({
|
||||
modelUuid: selectedAssets[0].userData.modelUuid,
|
||||
modelName: newName,
|
||||
projectId
|
||||
}).then(() => {
|
||||
selectedAssets[0].userData = {
|
||||
...selectedAssets[0].userData,
|
||||
modelName: newName
|
||||
};
|
||||
setAssetsApi(selectedAssets);
|
||||
setIsRenameMode(false);
|
||||
setName(selectedAssets[0].userData.modelUuid, newName);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -135,7 +154,7 @@ function MainScene() {
|
||||
{(isPlaying) &&
|
||||
activeModule !== "simulation" && <ControlsPlayer />}
|
||||
|
||||
{isRenameMode && selectedFloorItem?.userData.modelName && <RenameTooltip name={selectedFloorItem?.userData.modelName} onSubmit={handleObjectRename} />}
|
||||
{isRenameMode && (selectedFloorItem?.userData.modelName || selectedAssets.length === 1) && <RenameTooltip name={selectedFloorItem?.userData.modelName || selectedAssets[0].userData.modelName} onSubmit={handleObjectRename} />}
|
||||
{/* remove this later */}
|
||||
{activeModule === "builder" && !toggleThreeD && <SelectFloorPlan />}
|
||||
</>
|
||||
@@ -188,7 +207,7 @@ function MainScene() {
|
||||
{activeModule !== "market" && !selectedUser && <Footer />}
|
||||
|
||||
<VersionSaved />
|
||||
{(commentPositionState !== null || selectedComment !== null) && <ThreadChat/>}
|
||||
{(commentPositionState !== null || selectedComment !== null) && <ThreadChat />}
|
||||
|
||||
</>
|
||||
);
|
||||
|
||||
207
app/src/components/ui/menu/contextMenu.tsx
Normal file
207
app/src/components/ui/menu/contextMenu.tsx
Normal file
@@ -0,0 +1,207 @@
|
||||
import React from "react";
|
||||
|
||||
type ContextMenuProps = {
|
||||
visibility: {
|
||||
rename: boolean;
|
||||
focus: boolean;
|
||||
flipX: boolean;
|
||||
flipZ: boolean;
|
||||
move: boolean;
|
||||
rotate: boolean;
|
||||
duplicate: boolean;
|
||||
copy: boolean;
|
||||
paste: boolean;
|
||||
modifier: boolean;
|
||||
group: boolean;
|
||||
array: boolean;
|
||||
delete: boolean;
|
||||
};
|
||||
onRename: () => void;
|
||||
onFocus: () => void;
|
||||
onFlipX: () => void;
|
||||
onFlipZ: () => void;
|
||||
onMove: () => void;
|
||||
onRotate: () => void;
|
||||
onDuplicate: () => void;
|
||||
onCopy: () => void;
|
||||
onPaste: () => void;
|
||||
onGroup: () => void;
|
||||
onArray: () => void;
|
||||
onDelete: () => void;
|
||||
};
|
||||
|
||||
const ContextMenu: React.FC<ContextMenuProps> = ({
|
||||
visibility,
|
||||
onRename,
|
||||
onFocus,
|
||||
onFlipX,
|
||||
onFlipZ,
|
||||
onMove,
|
||||
onRotate,
|
||||
onDuplicate,
|
||||
onCopy,
|
||||
onPaste,
|
||||
onGroup,
|
||||
onArray,
|
||||
onDelete,
|
||||
}) => {
|
||||
const styles: { [key: string]: React.CSSProperties } = {
|
||||
contextMenu: {
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
backgroundColor: "#2c2c2c",
|
||||
borderRadius: "6px",
|
||||
padding: "8px",
|
||||
color: "white",
|
||||
fontFamily: "sans-serif",
|
||||
fontSize: "14px",
|
||||
boxShadow: "0 0 8px rgba(0,0,0,0.4)",
|
||||
zIndex: 1000,
|
||||
},
|
||||
menuItem: {
|
||||
margin: "4px 0",
|
||||
},
|
||||
button: {
|
||||
background: "none",
|
||||
border: "none",
|
||||
color: "inherit",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
width: "180px",
|
||||
padding: "4px 8px",
|
||||
cursor: "pointer",
|
||||
},
|
||||
submenu: {
|
||||
marginLeft: "10px",
|
||||
marginTop: "4px",
|
||||
backgroundColor: "#3a3a3a",
|
||||
borderRadius: "4px",
|
||||
padding: "4px",
|
||||
},
|
||||
shortcut: {
|
||||
opacity: 0.6,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={styles.contextMenu}>
|
||||
<div>
|
||||
{visibility.rename && (
|
||||
<div style={styles.menuItem}>
|
||||
<button style={styles.button} onClick={onRename}>
|
||||
<span>Rename</span>
|
||||
<span style={styles.shortcut}>F2</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{visibility.focus && (
|
||||
<div style={styles.menuItem}>
|
||||
<button style={styles.button} onClick={onFocus}>
|
||||
<span>Focus</span>
|
||||
<span style={styles.shortcut}>F</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{visibility.flipX && (
|
||||
<div style={styles.menuItem}>
|
||||
<button style={styles.button} onClick={onFlipX}>
|
||||
Flip to X axis
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{visibility.flipZ && (
|
||||
<div style={styles.menuItem}>
|
||||
<button style={styles.button} onClick={onFlipZ}>
|
||||
Flip to Z axis
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{(visibility.move || visibility.rotate) && (
|
||||
<div style={styles.menuItem}>
|
||||
<button style={styles.button}>Transform</button>
|
||||
<div style={styles.submenu}>
|
||||
{visibility.move && (
|
||||
<div style={styles.menuItem}>
|
||||
<button style={styles.button} onClick={onMove}>
|
||||
<span>Move</span>
|
||||
<span style={styles.shortcut}>G</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{visibility.rotate && (
|
||||
<div style={styles.menuItem}>
|
||||
<button style={styles.button} onClick={onRotate}>
|
||||
<span>Rotate</span>
|
||||
<span style={styles.shortcut}>R</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{visibility.duplicate && (
|
||||
<div style={styles.menuItem}>
|
||||
<button style={styles.button} onClick={onDuplicate}>
|
||||
<span>Duplicate</span>
|
||||
<span style={styles.shortcut}>Ctrl + D</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{visibility.copy && (
|
||||
<div style={styles.menuItem}>
|
||||
<button style={styles.button} onClick={onCopy}>
|
||||
<span>Copy Objects</span>
|
||||
<span style={styles.shortcut}>Ctrl + C</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{visibility.paste && (
|
||||
<div style={styles.menuItem}>
|
||||
<button style={styles.button} onClick={onPaste}>
|
||||
<span>Paste Objects</span>
|
||||
<span style={styles.shortcut}>Ctrl + V</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{visibility.modifier && (
|
||||
<div style={styles.menuItem}>
|
||||
<button style={styles.button}>Modifiers</button>
|
||||
</div>
|
||||
)}
|
||||
{(visibility.group || visibility.array) && (
|
||||
<div style={styles.menuItem}>
|
||||
<button style={styles.button}>Group / Array</button>
|
||||
<div style={styles.submenu}>
|
||||
{visibility.group && (
|
||||
<div style={styles.menuItem}>
|
||||
<button style={styles.button} onClick={onGroup}>
|
||||
<span>Group</span>
|
||||
<span style={styles.shortcut}>Ctrl + G</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{visibility.array && (
|
||||
<div style={styles.menuItem}>
|
||||
<button style={styles.button} onClick={onArray}>
|
||||
Array
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{visibility.delete && (
|
||||
<div style={styles.menuItem}>
|
||||
<button style={styles.button} onClick={onDelete}>
|
||||
<span>Delete</span>
|
||||
<span style={styles.shortcut}>X</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContextMenu;
|
||||
@@ -0,0 +1,194 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { CameraControls, Html, ScreenSpace } from '@react-three/drei';
|
||||
import { useContextActionStore, useRenameModeStore, useSelectedAssets } from '../../../../store/builder/store';
|
||||
import ContextMenu from '../../../../components/ui/menu/contextMenu';
|
||||
|
||||
function ContextControls() {
|
||||
const { gl, controls } = useThree();
|
||||
const [canRender, setCanRender] = useState(false);
|
||||
const [visibility, setVisibility] = useState({ rename: true, focus: true, flipX: true, flipZ: true, move: true, rotate: true, duplicate: true, copy: true, paste: true, modifier: false, group: false, array: false, delete: true, });
|
||||
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
|
||||
const { selectedAssets } = useSelectedAssets();
|
||||
const { setContextAction } = useContextActionStore();
|
||||
const { setIsRenameMode } = useRenameModeStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAssets.length === 1) {
|
||||
setVisibility({
|
||||
rename: true,
|
||||
focus: true,
|
||||
flipX: true,
|
||||
flipZ: true,
|
||||
move: true,
|
||||
rotate: true,
|
||||
duplicate: true,
|
||||
copy: true,
|
||||
paste: true,
|
||||
modifier: false,
|
||||
group: false,
|
||||
array: false,
|
||||
delete: true,
|
||||
});
|
||||
} else if (selectedAssets.length > 1) {
|
||||
setVisibility({
|
||||
rename: false,
|
||||
focus: true,
|
||||
flipX: true,
|
||||
flipZ: true,
|
||||
move: true,
|
||||
rotate: true,
|
||||
duplicate: true,
|
||||
copy: true,
|
||||
paste: true,
|
||||
modifier: false,
|
||||
group: true,
|
||||
array: false,
|
||||
delete: true,
|
||||
});
|
||||
} else {
|
||||
setVisibility({
|
||||
rename: false,
|
||||
focus: false,
|
||||
flipX: false,
|
||||
flipZ: false,
|
||||
move: false,
|
||||
rotate: false,
|
||||
duplicate: false,
|
||||
copy: false,
|
||||
paste: false,
|
||||
modifier: false,
|
||||
group: false,
|
||||
array: false,
|
||||
delete: false,
|
||||
});
|
||||
}
|
||||
}, [selectedAssets]);
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const handleContextClick = (event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
if (selectedAssets.length > 0) {
|
||||
setMenuPosition({ x: event.clientX - gl.domElement.width / 2, y: event.clientY - gl.domElement.height / 2 });
|
||||
setCanRender(true);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = false;
|
||||
}
|
||||
} else {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (selectedAssets.length > 0) {
|
||||
canvasElement.addEventListener('contextmenu', handleContextClick)
|
||||
} else {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setMenuPosition({ x: 0, y: 0 });
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener('contextmenu', handleContextClick);
|
||||
};
|
||||
}, [gl, selectedAssets]);
|
||||
|
||||
const handleAssetRename = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("renameAsset");
|
||||
setIsRenameMode(true);
|
||||
}
|
||||
const handleAssetFocus = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("focusAsset");
|
||||
}
|
||||
const handleAssetMove = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("moveAsset")
|
||||
}
|
||||
const handleAssetRotate = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("rotateAsset")
|
||||
}
|
||||
const handleAssetCopy = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("copyAsset")
|
||||
}
|
||||
const handleAssetPaste = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("pasteAsset")
|
||||
}
|
||||
const handleAssetDelete = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("deleteAsset")
|
||||
}
|
||||
const handleAssetDuplicate = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("duplicateAsset")
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{canRender && (
|
||||
<ScreenSpace depth={1} >
|
||||
<Html
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: menuPosition.y,
|
||||
left: menuPosition.x,
|
||||
zIndex: 1000
|
||||
}}
|
||||
>
|
||||
<ContextMenu
|
||||
visibility={visibility}
|
||||
onRename={() => handleAssetRename()}
|
||||
onFocus={() => handleAssetFocus()}
|
||||
onFlipX={() => console.log("Flip to X")}
|
||||
onFlipZ={() => console.log("Flip to Z")}
|
||||
onMove={() => handleAssetMove()}
|
||||
onRotate={() => handleAssetRotate()}
|
||||
onDuplicate={() => handleAssetDuplicate()}
|
||||
onCopy={() => handleAssetCopy()}
|
||||
onPaste={() => handleAssetPaste()}
|
||||
onGroup={() => console.log("Group")}
|
||||
onArray={() => console.log("Array")}
|
||||
onDelete={() => handleAssetDelete()}
|
||||
/>
|
||||
</Html>
|
||||
</ScreenSpace>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default ContextControls;
|
||||
@@ -14,6 +14,7 @@ import TransformControl from "./transformControls/transformControls";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { getUserData } from "../../../functions/getUserData";
|
||||
|
||||
import ContextControls from "./contextControls/contextControls";
|
||||
import SelectionControls2D from "./selectionControls/selection2D/selectionControls2D";
|
||||
import UndoRedo2DControls from "./undoRedoControls/undoRedo2D/undoRedo2DControls";
|
||||
|
||||
@@ -146,6 +147,8 @@ export default function Controls() {
|
||||
|
||||
<TransformControl />
|
||||
|
||||
<ContextControls />
|
||||
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import * as THREE from "three";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { SkeletonUtils } from "three-stdlib";
|
||||
import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store";
|
||||
import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store";
|
||||
import * as Types from "../../../../../types/world/worldTypes";
|
||||
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
|
||||
import { useParams } from "react-router-dom";
|
||||
@@ -40,6 +40,7 @@ const CopyPasteControls3D = ({
|
||||
const [relativePositions, setRelativePositions] = useState<THREE.Vector3[]>([]);
|
||||
const [centerOffset, setCenterOffset] = useState<THREE.Vector3 | null>(null);
|
||||
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ left: false, right: false, });
|
||||
const { contextAction, setContextAction } = useContextActionStore()
|
||||
|
||||
const calculateRelativePositions = useCallback((objects: THREE.Object3D[]) => {
|
||||
if (objects.length === 0) return { center: new THREE.Vector3(), relatives: [] };
|
||||
@@ -57,6 +58,16 @@ const CopyPasteControls3D = ({
|
||||
return { center, relatives };
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (contextAction === "copyAsset") {
|
||||
setContextAction(null);
|
||||
copySelection()
|
||||
} else if (contextAction === "pasteAsset") {
|
||||
setContextAction(null);
|
||||
pasteCopiedObjects()
|
||||
}
|
||||
}, [contextAction])
|
||||
|
||||
useEffect(() => {
|
||||
if (!camera || !scene || toggleView) return;
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as THREE from "three";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { SkeletonUtils } from "three-stdlib";
|
||||
import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store";
|
||||
import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store";
|
||||
import * as Types from "../../../../../types/world/worldTypes";
|
||||
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
|
||||
import { useParams } from "react-router-dom";
|
||||
@@ -38,12 +38,20 @@ const DuplicationControls3D = ({
|
||||
const [initialPositions, setInitialPositions] = useState<Record<string, THREE.Vector3>>({});
|
||||
const [isDuplicating, setIsDuplicating] = useState(false);
|
||||
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ left: false, right: false, });
|
||||
const { contextAction, setContextAction } = useContextActionStore()
|
||||
|
||||
const calculateDragOffset = useCallback((point: THREE.Object3D, hitPoint: THREE.Vector3) => {
|
||||
const pointPosition = new THREE.Vector3().copy(point.position);
|
||||
return new THREE.Vector3().subVectors(pointPosition, hitPoint);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (contextAction === "duplicateAsset") {
|
||||
setContextAction(null);
|
||||
duplicateSelection()
|
||||
}
|
||||
}, [contextAction])
|
||||
|
||||
useEffect(() => {
|
||||
if (!camera || !scene || toggleView) return;
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as THREE from "three";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { useSelectedAssets, useSocketStore, useToggleView, } from "../../../../../store/builder/store";
|
||||
import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView, } from "../../../../../store/builder/store";
|
||||
import * as Types from "../../../../../types/world/worldTypes";
|
||||
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
|
||||
import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
@@ -47,6 +47,7 @@ function MoveControls3D({
|
||||
const [initialStates, setInitialStates] = useState<Record<string, { position: THREE.Vector3; rotation?: THREE.Euler; }>>({});
|
||||
const [isMoving, setIsMoving] = useState(false);
|
||||
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ left: false, right: false, });
|
||||
const { contextAction, setContextAction } = useContextActionStore()
|
||||
|
||||
const updateBackend = (
|
||||
productName: string,
|
||||
@@ -63,6 +64,13 @@ function MoveControls3D({
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (contextAction === "moveAsset") {
|
||||
setContextAction(null);
|
||||
moveAssets()
|
||||
}
|
||||
}, [contextAction])
|
||||
|
||||
useEffect(() => {
|
||||
if (!camera || !scene || toggleView) return;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as THREE from "three";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store";
|
||||
import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store";
|
||||
import * as Types from "../../../../../types/world/worldTypes";
|
||||
import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import { useParams } from "react-router-dom";
|
||||
@@ -42,10 +42,8 @@ function RotateControls3D({
|
||||
const [isRotating, setIsRotating] = useState(false);
|
||||
const prevPointerPosition = useRef<THREE.Vector2 | null>(null);
|
||||
const rotationCenter = useRef<THREE.Vector3 | null>(null);
|
||||
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({
|
||||
left: false,
|
||||
right: false,
|
||||
});
|
||||
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ left: false, right: false, });
|
||||
const { contextAction, setContextAction } = useContextActionStore()
|
||||
|
||||
const updateBackend = useCallback((
|
||||
productName: string,
|
||||
@@ -62,6 +60,13 @@ function RotateControls3D({
|
||||
});
|
||||
}, [selectedVersion]);
|
||||
|
||||
useEffect(() => {
|
||||
if (contextAction === "rotateAsset") {
|
||||
setContextAction(null);
|
||||
rotateAssets()
|
||||
}
|
||||
}, [contextAction])
|
||||
|
||||
useEffect(() => {
|
||||
if (!camera || !scene || toggleView) return;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { getUserData } from "../../../../../functions/getUserData";
|
||||
import { useSceneContext } from "../../../sceneContext";
|
||||
import { useVersionContext } from "../../../../builder/version/versionContext";
|
||||
import { useProductContext } from "../../../../simulation/products/productContext";
|
||||
import { useSelectedAssets, useSocketStore, useToggleView, useToolMode, } from "../../../../../store/builder/store";
|
||||
import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView, useToolMode, } from "../../../../../store/builder/store";
|
||||
import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import DuplicationControls3D from "./duplicationControls3D";
|
||||
import CopyPasteControls3D from "./copyPasteControls3D";
|
||||
@@ -33,6 +33,7 @@ const SelectionControls3D: React.FC = () => {
|
||||
const { socket } = useSocketStore();
|
||||
const { assetStore, eventStore, productStore } = useSceneContext();
|
||||
const { removeAsset } = assetStore();
|
||||
const { contextAction, setContextAction } = useContextActionStore()
|
||||
const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]);
|
||||
const { toolMode } = useToolMode();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
@@ -65,6 +66,13 @@ const SelectionControls3D: React.FC = () => {
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (contextAction === "deleteAsset") {
|
||||
setContextAction(null);
|
||||
deleteSelection()
|
||||
}
|
||||
}, [contextAction])
|
||||
|
||||
useEffect(() => {
|
||||
if (!camera || !scene || toggleView) return;
|
||||
|
||||
@@ -107,7 +115,7 @@ const SelectionControls3D: React.FC = () => {
|
||||
if (event.button === 2 && !event.ctrlKey && !event.shiftKey) {
|
||||
isRightClick.current = false;
|
||||
if (!rightClickMoved.current) {
|
||||
clearSelection();
|
||||
// clearSelection();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -192,7 +200,7 @@ const SelectionControls3D: React.FC = () => {
|
||||
const onContextMenu = (event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
if (!rightClickMoved.current) {
|
||||
clearSelection();
|
||||
// clearSelection();
|
||||
}
|
||||
rightClickMoved.current = false;
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ function UndoRedo2DControls() {
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
|
||||
useEffect(() => {
|
||||
console.log(undoStack, redoStack);
|
||||
// console.log(undoStack, redoStack);
|
||||
}, [undoStack, redoStack]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Object3D } from "three";
|
||||
import { create } from "zustand";
|
||||
import { io } from "socket.io-client";
|
||||
import * as CONSTANTS from "../../types/world/worldConstants";
|
||||
@@ -166,9 +167,14 @@ export const useNavMesh = create<any>((set: any) => ({
|
||||
setNavMesh: (x: any) => set({ navMesh: x }),
|
||||
}));
|
||||
|
||||
export const useSelectedAssets = create<any>((set: any) => ({
|
||||
type SelectedAssetsState = {
|
||||
selectedAssets: Object3D[];
|
||||
setSelectedAssets: (assets: Object3D[]) => void;
|
||||
};
|
||||
|
||||
export const useSelectedAssets = create<SelectedAssetsState>((set) => ({
|
||||
selectedAssets: [],
|
||||
setSelectedAssets: (x: any) => set(() => ({ selectedAssets: x })),
|
||||
setSelectedAssets: (assets) => set({ selectedAssets: assets }),
|
||||
}));
|
||||
|
||||
export const useLayers = create<any>((set: any) => ({
|
||||
@@ -632,3 +638,7 @@ export const useSelectedPath = create<any>((set: any) => ({
|
||||
selectedPath: "auto",
|
||||
setSelectedPath: (x: any) => set({ selectedPath: x }),
|
||||
}));
|
||||
export const useContextActionStore = create<any>((set: any) => ({
|
||||
contextAction: null,
|
||||
setContextAction: (x: any) => set({ contextAction: x }),
|
||||
}));
|
||||
|
||||
@@ -11,6 +11,7 @@ import useVersionHistoryVisibleStore, {
|
||||
useDfxUpload,
|
||||
useRenameModeStore,
|
||||
useSaveVersion,
|
||||
useSelectedAssets,
|
||||
useSelectedComment,
|
||||
useSelectedFloorItem,
|
||||
useSelectedWallItem,
|
||||
@@ -50,6 +51,7 @@ const KeyPressListener: React.FC = () => {
|
||||
const { setViewSceneLabels } = useViewSceneStore();
|
||||
const { isRenameMode, setIsRenameMode } = useRenameModeStore();
|
||||
const { selectedFloorItem } = useSelectedFloorItem();
|
||||
const { selectedAssets } = useSelectedAssets();
|
||||
const { setCreateNewVersion } = useVersionHistoryStore();
|
||||
const { setVersionHistoryVisible } = useVersionHistoryVisibleStore();
|
||||
const { setSelectedComment } = useSelectedComment();
|
||||
@@ -254,7 +256,7 @@ const KeyPressListener: React.FC = () => {
|
||||
setViewSceneLabels((prev) => !prev);
|
||||
}
|
||||
|
||||
if (selectedFloorItem && keyCombination === "F2") {
|
||||
if ((selectedFloorItem || selectedAssets.length === 1) && keyCombination === "F2") {
|
||||
setIsRenameMode(true);
|
||||
}
|
||||
|
||||
@@ -281,6 +283,7 @@ const KeyPressListener: React.FC = () => {
|
||||
hidePlayer,
|
||||
selectedFloorItem,
|
||||
isRenameMode,
|
||||
selectedAssets
|
||||
]);
|
||||
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user