import { observer } from "mobx-react-lite"; import type { ObjectInstance, R3, Runtime } from "../types"; import { useMemo, useRef, type RefObject } from "react"; import type { Group } from "three"; import { TransformControls, useHelper } from "@react-three/drei"; import { BoxHelper } from "three"; import type { ThreeEvent } from "@react-three/fiber"; import { state } from "../state"; import { nextSelectionEditMode } from "../state/worldEditorState"; import { ObjectViewInternal, type ObjectViewInternalHandle } from "./ObjectViewInternal"; type ObjectEditorViewProps = { object: Runtime; } type SelectionOverlayProps = { objectId: string; ref: RefObject; onTransformEnd: () => void; } // Separate observer so only the 2 affected instances (selected/deselected) // re-render on selection change, not all N objects in the scene. const SelectionOverlay = observer(function ({ objectId, ref, onTransformEnd }: SelectionOverlayProps) { const isSelected = state.worldEditor.isEnabled && state.worldEditor.selectedObjectId === objectId; const selectionMode = isSelected ? state.worldEditor.selectedObjectMode : undefined; // Stable virtual ref that reads through to the group inside the handle const groupRef = useMemo>( () => ({ get current() { return ref.current?.group ?? null; } }), [ref], ); useHelper(isSelected ? groupRef : { current: null } as any, BoxHelper, 'white'); if (!isSelected || selectionMode === undefined || !groupRef.current) return null; return ( } mode={selectionMode} onMouseUp={onTransformEnd} /> ); }); export const ObjectEditorView = observer(function ({ object }: ObjectEditorViewProps) { const dataRef = useRef(null); // Only observes world object types — not selection state — so won't // re-render when a different object is selected. const objectType = state.world.getObjectTypeById(object.typeId); if (!objectType) return null; function handleClick(e: ThreeEvent) { if (e.delta > 5) return; e.stopPropagation(); // Reading selection state inside an event handler: not tracked by observer. const currentMode = state.worldEditor.isEnabled && state.worldEditor.selectedObjectId === object.id ? state.worldEditor.selectedObjectMode : undefined; state.worldEditor.setSelectedObject(object.id, nextSelectionEditMode(currentMode)); } function handleTransformEnd() { const group = dataRef.current?.group; if (group) state.worldEditor.setObjectTransform( object.id, group.position.toArray(), group.rotation.toArray().slice(0, 3) as R3, group.scale.toArray(), ); } return (<> ); });