Merge remote-tracking branch 'origin/ui' into main-demo
This commit is contained in:
@@ -55,6 +55,7 @@ interface ShortcutHelperProps {
|
||||
const ShortcutHelper: React.FC<ShortcutHelperProps> = ({
|
||||
setShowShortcuts,
|
||||
}) => {
|
||||
|
||||
const shortcuts: ShortcutGroup[] = [
|
||||
// Essential
|
||||
{
|
||||
@@ -310,6 +311,7 @@ const ShortcutHelper: React.FC<ShortcutHelperProps> = ({
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
|
||||
<div className="header">
|
||||
<div className="header-wrapper">
|
||||
{shortcuts.map((group) => (
|
||||
@@ -326,9 +328,8 @@ const ShortcutHelper: React.FC<ShortcutHelperProps> = ({
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`shortcut-wrapper ${
|
||||
activeShortcuts.length === 1 ? "single-item" : ""
|
||||
}`}
|
||||
className={`shortcut-wrapper ${activeShortcuts.length === 1 ? "single-item" : ""
|
||||
}`}
|
||||
>
|
||||
{activeShortcuts.map((item) => (
|
||||
<div
|
||||
|
||||
@@ -212,7 +212,11 @@ function MainScene() {
|
||||
{activeModule !== "market" && !selectedUser && <Footer />}
|
||||
|
||||
<VersionSaved />
|
||||
{(commentPositionState !== null || selectedComment !== null) && <ThreadChat />}
|
||||
|
||||
{
|
||||
(commentPositionState !== null || selectedComment !== null) &&
|
||||
<ThreadChat />
|
||||
}
|
||||
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -231,7 +231,10 @@ const Messages: React.FC<MessageProps> = ({ val, i, setMessages, mode, setIsEdit
|
||||
<div className="time">{isEditableThread ? getRelativeTime(val.createdAt) : val.createdAt}</div>
|
||||
</div>
|
||||
{(val as Reply).creatorId === userId && (
|
||||
<div className="more-options">
|
||||
<div
|
||||
className="more-options"
|
||||
onMouseLeave={() => setOpenOptions(false)}
|
||||
>
|
||||
<button
|
||||
className="more-options-button"
|
||||
onClick={() => {
|
||||
@@ -240,35 +243,41 @@ const Messages: React.FC<MessageProps> = ({ val, i, setMessages, mode, setIsEdit
|
||||
>
|
||||
<KebabIcon />
|
||||
</button>
|
||||
|
||||
{openOptions && (
|
||||
<div className="options-list">
|
||||
<button
|
||||
className="option"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setMode && setMode("edit")
|
||||
setMode && setMode("edit");
|
||||
setOpenOptions(false);
|
||||
setEditedThread && setEditedThread(true);
|
||||
setIsEditComment(true)
|
||||
setIsEditComment(true);
|
||||
}}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
{!(isEditableThread) && <button
|
||||
className="option"
|
||||
onClick={() => {
|
||||
handleDeleteAction((val as Reply).replyId);
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</button>}
|
||||
|
||||
{!isEditableThread && (
|
||||
<button
|
||||
className="option"
|
||||
onClick={() => {
|
||||
handleDeleteAction((val as Reply).replyId);
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="message">
|
||||
{"comment" in val ? val.comment : val.threadTitle}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div >
|
||||
)}
|
||||
|
||||
@@ -131,9 +131,10 @@ const ThreadChat: React.FC = () => {
|
||||
if (dragging) updatePosition(e, true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
updatePosition({ clientX: position.x, clientY: position.y }, true);
|
||||
}, [selectedComment]);
|
||||
// Commented this useEffect to prevent offset after user saved the comment
|
||||
// useEffect(() => {
|
||||
// updatePosition({ clientX: position.x, clientY: position.y }, true);
|
||||
// }, [selectedComment]);
|
||||
|
||||
|
||||
const handlePointerUp = (event: React.PointerEvent<HTMLDivElement>) => {
|
||||
@@ -144,6 +145,10 @@ const ThreadChat: React.FC = () => {
|
||||
};
|
||||
|
||||
const handleCreateComments = async (e: any) => {
|
||||
// Continue send or create message only there is only value avalibale
|
||||
// To prevent empty value
|
||||
|
||||
if (!value) return;
|
||||
e.preventDefault();
|
||||
try {
|
||||
// const createComments = await addCommentsApi(projectId, value, selectedComment?.threadId, selectedVersion?.versionId || "")/
|
||||
@@ -163,6 +168,7 @@ const ThreadChat: React.FC = () => {
|
||||
|
||||
// }
|
||||
|
||||
|
||||
if (threadSocket && mode === "create") {
|
||||
const addComment = {
|
||||
versionId: selectedVersion?.versionId || "",
|
||||
@@ -190,7 +196,7 @@ const ThreadChat: React.FC = () => {
|
||||
// removeComment(selectedComment?.threadId)
|
||||
// setSelectedComment([])
|
||||
// }
|
||||
console.log('threadSocket:threadChat ', threadSocket);
|
||||
|
||||
if (threadSocket) {
|
||||
// projectId, userId, organization, threadId
|
||||
const deleteThread = {
|
||||
@@ -258,7 +264,7 @@ const ThreadChat: React.FC = () => {
|
||||
};
|
||||
|
||||
if (threadSocket) {
|
||||
console.log('createThread: ', createThread);
|
||||
|
||||
|
||||
setInputActive(false);
|
||||
threadSocket.emit("v1:thread:create", createThread);
|
||||
|
||||
47
app/src/hooks/useCameraShortcuts.ts
Normal file
47
app/src/hooks/useCameraShortcuts.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { useEffect } from "react";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import * as THREE from "three";
|
||||
import type { CameraControls } from "@react-three/drei";
|
||||
|
||||
export const useCameraShortcuts = (controlsRef: React.RefObject<CameraControls>) => {
|
||||
const { camera } = useThree();
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (!controlsRef.current) return;
|
||||
|
||||
// get current distance from camera to target
|
||||
const target = new THREE.Vector3();
|
||||
controlsRef.current.getTarget(target);
|
||||
|
||||
const distance = camera.position.distanceTo(target);
|
||||
let pos: THREE.Vector3 | null = null;
|
||||
|
||||
switch (e.key) {
|
||||
case "1": // Front
|
||||
pos = new THREE.Vector3(0, 0, distance).add(target);
|
||||
break;
|
||||
case "3": // Right
|
||||
pos = new THREE.Vector3(distance, 0, 0).add(target);
|
||||
break;
|
||||
case "7": // Top
|
||||
pos = new THREE.Vector3(0, distance, 0).add(target);
|
||||
break;
|
||||
case "9": // Back
|
||||
pos = new THREE.Vector3(0, 0, -distance).add(target);
|
||||
break;
|
||||
}
|
||||
|
||||
if (pos) {
|
||||
controlsRef.current.setLookAt(
|
||||
pos.x, pos.y, pos.z, // camera position
|
||||
target.x, target.y, target.z, // keep same target
|
||||
true // smooth transition
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||
}, [controlsRef, camera]);
|
||||
};
|
||||
@@ -57,6 +57,7 @@ export default function Builder() {
|
||||
const { setHoveredPoint, setHoveredLine } = useBuilderStore();
|
||||
const { userId, organization } = getUserData();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (!toggleView) {
|
||||
setHoveredLine(null);
|
||||
|
||||
@@ -18,6 +18,7 @@ import ContextControls from "./contextControls/contextControls";
|
||||
import SelectionControls2D from "./selectionControls/selection2D/selectionControls2D";
|
||||
import UndoRedo2DControls from "./undoRedoControls/undoRedo2D/undoRedo2DControls";
|
||||
import UndoRedo3DControls from "./undoRedoControls/undoRedo3D/undoRedo3DControls";
|
||||
import { useCameraShortcuts } from "../../../hooks/useCameraShortcuts";
|
||||
|
||||
export default function Controls() {
|
||||
const controlsRef = useRef<CameraControls>(null);
|
||||
@@ -116,6 +117,7 @@ export default function Controls() {
|
||||
stopInterval();
|
||||
};
|
||||
}, [toggleView, state, socket]);
|
||||
useCameraShortcuts(controlsRef);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -241,21 +241,18 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
||||
{Object.keys(zonesData).length !== 0 ? (
|
||||
<>
|
||||
{Object.values(zonesData).map((zone, index) => (
|
||||
<>
|
||||
{ }
|
||||
<div
|
||||
key={index}
|
||||
className={`zone ${selectedZone.zoneUuid === zone.zoneUuid ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
<div
|
||||
key={`${index}_${zone.zoneName}`}
|
||||
className={`zone ${selectedZone.zoneUuid === zone.zoneUuid ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
|
||||
handleSelect2dZoneData(zonesData[zone.zoneUuid]?.zoneUuid, zone.zoneName)
|
||||
}
|
||||
}
|
||||
>
|
||||
{zone.zoneName}
|
||||
</div>
|
||||
</>
|
||||
handleSelect2dZoneData(zonesData[zone.zoneUuid]?.zoneUuid, zone.zoneName)
|
||||
}
|
||||
}
|
||||
>
|
||||
{zone.zoneName}
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -516,6 +516,7 @@
|
||||
width: 50px;
|
||||
border-radius: 100px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
outline: 1px solid var(--accent-color);
|
||||
}
|
||||
@@ -586,6 +587,7 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 8px;
|
||||
|
||||
&:hover {
|
||||
background: var(--background-color);
|
||||
outline: 1px solid #aaaaaa29;
|
||||
@@ -600,6 +602,7 @@
|
||||
|
||||
.kebab-icon {
|
||||
display: flex;
|
||||
|
||||
svg {
|
||||
transform: rotate(90deg) scale(0.8);
|
||||
}
|
||||
@@ -1434,6 +1437,11 @@
|
||||
padding: 12px;
|
||||
border-radius: #{$border-radius-large};
|
||||
|
||||
outline: 1px solid var(--border-color);
|
||||
outline-offset: -1px;
|
||||
border-radius: 12px;
|
||||
background: var(--background-color);
|
||||
|
||||
.compare-simulations-header {
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
@@ -1598,12 +1606,13 @@
|
||||
.animations-lists {
|
||||
max-height: 210px;
|
||||
overflow: auto;
|
||||
.no-animation{
|
||||
.no-animation {
|
||||
padding: 6px 8px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.animations-list-wrapper {
|
||||
padding: 0 4px;
|
||||
|
||||
.animations-list {
|
||||
margin: 2px 0;
|
||||
padding: 4px 12px;
|
||||
@@ -1793,7 +1802,8 @@
|
||||
padding: 14px;
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 8px;
|
||||
.input-toggle-container{
|
||||
|
||||
.input-toggle-container {
|
||||
padding: 4px 0;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
@@ -171,8 +171,8 @@
|
||||
.messages-wrapper {
|
||||
padding: 12px;
|
||||
padding-top: 0;
|
||||
max-height: 50vh;
|
||||
overflow-y: auto;
|
||||
max-height: 36vh;
|
||||
overflow: auto;
|
||||
.edit-container {
|
||||
.input-container {
|
||||
textarea {
|
||||
|
||||
@@ -221,8 +221,12 @@ const KeyPressListener: React.FC = () => {
|
||||
|
||||
// Shortcuts specific for sidebar visibility toggle and others specific to sidebar if added
|
||||
handleSidebarShortcuts(keyCombination);
|
||||
|
||||
|
||||
// Active module selection (builder, simulation, etc.)
|
||||
handleModuleSwitch(keyCombination);
|
||||
if (event.location === 0) { // Location 0 = standard keyboard (not numpad)
|
||||
handleModuleSwitch(keyCombination);
|
||||
}
|
||||
// Common editing tools: cursor | delete | free-hand
|
||||
handlePrimaryTools(keyCombination);
|
||||
// Shortcuts specific to the builder module (e.g., drawing and measurement tools)
|
||||
|
||||
Reference in New Issue
Block a user