feat(review): wire InlineCommentComposer into Editor with anchored positioning
This commit is contained in:
@@ -28,6 +28,8 @@ import Minimap from '../components/Minimap';
|
|||||||
import UploadPanel from '../components/UploadPanel';
|
import UploadPanel from '../components/UploadPanel';
|
||||||
import ExportDialog from '../components/ExportDialog';
|
import ExportDialog from '../components/ExportDialog';
|
||||||
import FeedbackPanel from '../components/feedback/FeedbackPanel';
|
import FeedbackPanel from '../components/feedback/FeedbackPanel';
|
||||||
|
import InlineCommentComposer from '../components/feedback/InlineCommentComposer';
|
||||||
|
import { useAnchoredOverlayPosition } from '../hooks/useAnchoredOverlayPosition';
|
||||||
import { UploadManager } from '../stores/uploadManager';
|
import { UploadManager } from '../stores/uploadManager';
|
||||||
import { InboxZone } from '../canvas/InboxZone';
|
import { InboxZone } from '../canvas/InboxZone';
|
||||||
import { getItemWorldBounds } from '../canvas/SceneManager';
|
import { getItemWorldBounds } from '../canvas/SceneManager';
|
||||||
@@ -111,6 +113,7 @@ export default function Editor({ isPublicView }: EditorProps) {
|
|||||||
const expandSeqRef = useRef(0);
|
const expandSeqRef = useRef(0);
|
||||||
const [draftCommentText, setDraftCommentText] = useState('');
|
const [draftCommentText, setDraftCommentText] = useState('');
|
||||||
const [creatingPointThread, setCreatingPointThread] = useState(false);
|
const [creatingPointThread, setCreatingPointThread] = useState(false);
|
||||||
|
const [sceneVersion, setSceneVersion] = useState(0);
|
||||||
const [focusMode, setFocusMode] = useState(false);
|
const [focusMode, setFocusMode] = useState(false);
|
||||||
const [layerList, setLayerList] = useState<any[]>([]);
|
const [layerList, setLayerList] = useState<any[]>([]);
|
||||||
const [selectedLayerIds, setSelectedLayerIds] = useState<string[]>([]);
|
const [selectedLayerIds, setSelectedLayerIds] = useState<string[]>([]);
|
||||||
@@ -150,6 +153,7 @@ export default function Editor({ isPublicView }: EditorProps) {
|
|||||||
// Omit for structural changes — falls through to debounced full scene sync.
|
// Omit for structural changes — falls through to debounced full scene sync.
|
||||||
const onCanvasChange = useCallback((changedIds?: string[]) => {
|
const onCanvasChange = useCallback((changedIds?: string[]) => {
|
||||||
scheduleSave();
|
scheduleSave();
|
||||||
|
setSceneVersion(v => v + 1);
|
||||||
if (changedIds && changedIds.length > 0) {
|
if (changedIds && changedIds.length > 0) {
|
||||||
// Incremental: broadcast only changed elements
|
// Incremental: broadcast only changed elements
|
||||||
syncRef.current?.broadcastElements(changedIds);
|
syncRef.current?.broadcastElements(changedIds);
|
||||||
@@ -204,6 +208,15 @@ export default function Editor({ isPublicView }: EditorProps) {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [objectCount]);
|
}, [objectCount]);
|
||||||
|
|
||||||
|
// Inline composer position — tracks viewport + scene changes for anchored placement
|
||||||
|
const composerAnchor = draftPin ? { objectId: draftPin.objectId, pinX: draftPin.pinX, pinY: draftPin.pinY } : null;
|
||||||
|
const composerPos = useAnchoredOverlayPosition(
|
||||||
|
canvasRef.current?.getViewport() ?? null,
|
||||||
|
canvasRef.current?.getScene() ?? null,
|
||||||
|
composerAnchor,
|
||||||
|
sceneVersion,
|
||||||
|
);
|
||||||
|
|
||||||
// Keep reviewModeRef in sync for tool handlers to read
|
// Keep reviewModeRef in sync for tool handlers to read
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reviewModeRef.current = reviewMode;
|
reviewModeRef.current = reviewMode;
|
||||||
@@ -960,6 +973,32 @@ export default function Editor({ isPublicView }: EditorProps) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Inline comment composer — anchored to draft pin */}
|
||||||
|
{reviewMode && draftPin && (
|
||||||
|
<InlineCommentComposer
|
||||||
|
visible={composerPos.visible}
|
||||||
|
x={composerPos.screenX}
|
||||||
|
y={composerPos.screenY}
|
||||||
|
placement={composerPos.placement}
|
||||||
|
objectLabel={
|
||||||
|
canvasObjectMap.get(draftPin.objectId)?.name ||
|
||||||
|
canvasObjectMap.get(draftPin.objectId)?.type ||
|
||||||
|
'Object'
|
||||||
|
}
|
||||||
|
value={draftCommentText}
|
||||||
|
onChange={setDraftCommentText}
|
||||||
|
onSubmit={async () => {
|
||||||
|
if (!draftPin || !draftCommentText.trim()) return;
|
||||||
|
await handleCreatePointThread(draftPin, draftCommentText.trim());
|
||||||
|
}}
|
||||||
|
onCancel={() => {
|
||||||
|
setDraftPin(null);
|
||||||
|
setDraftCommentText('');
|
||||||
|
}}
|
||||||
|
submitting={creatingPointThread}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Context menu */}
|
{/* Context menu */}
|
||||||
{contextMenu && (
|
{contextMenu && (
|
||||||
<ContextMenu
|
<ContextMenu
|
||||||
|
|||||||
Reference in New Issue
Block a user