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;
|
||||
Reference in New Issue
Block a user