Consolidate multi-line comment inputs in Plan and File views* (#371)

* refactor: adapt BlockWidget to use shared container map

Enable BlockWidget to reuse DOM containers via a shared map
Pass container map into widget decorations to preserve portals across updates

* feat: enable drag-to-select and guard new comments in FilesView

Guard new comment input from opening during drag-to-select actions
Enable drag-based line selection with initial drag state and mobile support
Reset dragging state on mouseup to end the selection

* fix(PlanView): gate new comment input during dragging

Add drag state to disable new comment input during selection drag
Indicate drag mode during mobile tap-extend to avoid unintended actions
Pass drag state to child UI to reflect ongoing drag
This commit is contained in:
Nelson Pires
2026-02-09 16:52:19 +02:00
committed by GitHub
parent f65388cf13
commit 8d8abdb54b
3 changed files with 74 additions and 68 deletions
+30 -24
View File
@@ -647,6 +647,7 @@ export const FilesView: React.FC = () => {
const [lineSelection, setLineSelection] = React.useState<SelectedLineRange | null>(null);
const isSelectingRef = React.useRef(false);
const selectionStartRef = React.useRef<number | null>(null);
const [isDragging, setIsDragging] = React.useState(false);
// Session/config for sending comments
const currentSessionId = useSessionStore((state) => state.currentSessionId);
@@ -663,6 +664,7 @@ export const FilesView: React.FC = () => {
const handleGlobalMouseUp = () => {
isSelectingRef.current = false;
selectionStartRef.current = null;
setIsDragging(false);
};
document.addEventListener('mouseup', handleGlobalMouseUp);
return () => document.removeEventListener('mouseup', handleGlobalMouseUp);
@@ -1805,10 +1807,10 @@ export const FilesView: React.FC = () => {
});
// Add input for new comment
if (lineSelection && !editingDraftId) {
if (lineSelection && !editingDraftId && !isDragging) {
widgets.push({
afterLine: lineSelection.end,
id: 'new-comment-input',
id: 'files-new-comment-input',
content: (
<InlineCommentInput
lineRange={lineSelection}
@@ -1820,7 +1822,7 @@ export const FilesView: React.FC = () => {
}
return widgets;
}, [selectedFile, currentSessionId, allDrafts, editingDraftId, lineSelection, handleSaveComment, removeDraft]);
}, [selectedFile, currentSessionId, allDrafts, editingDraftId, lineSelection, handleSaveComment, removeDraft, isDragging]);
const fileViewer = (
<div
@@ -2205,19 +2207,21 @@ export const FilesView: React.FC = () => {
const lineNumber = view.state.doc.lineAt(line.from).number;
// Mobile: tap-to-extend selection
if (isMobile && lineSelection && !event.shiftKey) {
const start = Math.min(lineSelection.start, lineSelection.end, lineNumber);
const end = Math.max(lineSelection.start, lineSelection.end, lineNumber);
setLineSelection({ start, end });
isSelectingRef.current = false;
selectionStartRef.current = null;
return true;
}
if (isMobile && lineSelection && !event.shiftKey) {
const start = Math.min(lineSelection.start, lineSelection.end, lineNumber);
const end = Math.max(lineSelection.start, lineSelection.end, lineNumber);
setLineSelection({ start, end });
isSelectingRef.current = false;
selectionStartRef.current = null;
setIsDragging(false);
return true;
}
isSelectingRef.current = true;
selectionStartRef.current = lineNumber;
isSelectingRef.current = true;
selectionStartRef.current = lineNumber;
setIsDragging(true);
if (lineSelection && event.shiftKey) {
if (lineSelection && event.shiftKey) {
const start = Math.min(lineSelection.start, lineNumber);
const end = Math.max(lineSelection.end, lineNumber);
setLineSelection({ start, end });
@@ -2239,17 +2243,19 @@ export const FilesView: React.FC = () => {
}
const lineNumber = view.state.doc.lineAt(line.from).number;
const start = Math.min(selectionStartRef.current, lineNumber);
const end = Math.max(selectionStartRef.current, lineNumber);
setLineSelection({ start, end });
return false;
const start = Math.min(selectionStartRef.current, lineNumber);
const end = Math.max(selectionStartRef.current, lineNumber);
setLineSelection({ start, end });
setIsDragging(true);
return false;
},
mouseup: () => {
isSelectingRef.current = false;
selectionStartRef.current = null;
setIsDragging(false);
return false;
},
},
mouseup: () => {
isSelectingRef.current = false;
selectionStartRef.current = null;
return false;
},
},
}}
/>
</div>
+20 -12
View File
@@ -156,11 +156,13 @@ export const PlanView: React.FC = () => {
}, []);
const isSelectingRef = React.useRef(false);
const selectionStartRef = React.useRef<number | null>(null);
const [isDragging, setIsDragging] = React.useState(false);
React.useEffect(() => {
const handleGlobalMouseUp = () => {
isSelectingRef.current = false;
selectionStartRef.current = null;
setIsDragging(false);
};
document.addEventListener('mouseup', handleGlobalMouseUp);
return () => document.removeEventListener('mouseup', handleGlobalMouseUp);
@@ -428,10 +430,10 @@ export const PlanView: React.FC = () => {
});
// Add new comment input if selecting AND not editing an existing draft
if (lineSelection && !editingDraftId) {
if (lineSelection && !editingDraftId && !isDragging) {
widgets.push({
afterLine: lineSelection.end,
id: 'new-comment-input',
id: 'plan-new-comment-input',
content: (
<InlineCommentInput
fileLabel={fileLabel}
@@ -457,6 +459,7 @@ export const PlanView: React.FC = () => {
handleSaveComment,
handleCancelComment,
removeDraft,
isDragging,
]);
return (
@@ -593,11 +596,14 @@ export const PlanView: React.FC = () => {
setLineSelection({ start, end });
isSelectingRef.current = false;
selectionStartRef.current = null;
// Mobile tap-extend is atomic, so we don't start drag
setIsDragging(false);
return true;
}
isSelectingRef.current = true;
selectionStartRef.current = lineNumber;
setIsDragging(true);
if (lineSelection && event.shiftKey) {
const start = Math.min(lineSelection.start, lineNumber);
@@ -614,17 +620,19 @@ export const PlanView: React.FC = () => {
if (event.buttons !== 1) return false;
if (!isSelectingRef.current || selectionStartRef.current === null) return false;
const lineNumber = view.state.doc.lineAt(line.from).number;
const start = Math.min(selectionStartRef.current, lineNumber);
const end = Math.max(selectionStartRef.current, lineNumber);
setLineSelection({ start, end });
return false;
const start = Math.min(selectionStartRef.current, lineNumber);
const end = Math.max(selectionStartRef.current, lineNumber);
setLineSelection({ start, end });
setIsDragging(true);
return false;
},
mouseup: () => {
isSelectingRef.current = false;
selectionStartRef.current = null;
setIsDragging(false);
return false;
},
},
mouseup: () => {
isSelectingRef.current = false;
selectionStartRef.current = null;
return false;
},
},
}}
/>
</div>