Fixing scrollbar flickering in annotator mode (#1968)

<!-- This is an auto-generated description by cubic. -->
## Summary by cubic
Fixes scrollbar flickering in annotator mode by constraining draggable
inputs within the container and suppressing scroll during drag for
smoother movement.

- **Bug Fixes**
- Added containerRef to DraggableTextInput and elementRef to calculate
bounds.
- Constrained drag coordinates to container size and accounted for
scale.
- Prevented default and stopped propagation on mousemove to avoid scroll
jitter.
  - Passed containerRef from Annotator to DraggableTextInput.

<sup>Written for commit 959605ddaa5faf23252ee797bf206c6dff46a069.
Summary will update automatically on new commits.</sup>

<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Mohamed Aziz Mejri
2025-12-16 20:21:33 +01:00
committed by GitHub
parent 3fd45ec253
commit 2e31c508da
2 changed files with 25 additions and 4 deletions

View File

@@ -26,6 +26,7 @@ interface DraggableTextInputProps {
spanRef: React.MutableRefObject<HTMLSpanElement[]>; spanRef: React.MutableRefObject<HTMLSpanElement[]>;
inputRef: React.MutableRefObject<HTMLInputElement[]>; inputRef: React.MutableRefObject<HTMLInputElement[]>;
color: string; color: string;
containerRef?: React.RefObject<HTMLDivElement | null>;
} }
export const DraggableTextInput = ({ export const DraggableTextInput = ({
@@ -40,15 +41,33 @@ export const DraggableTextInput = ({
spanRef, spanRef,
inputRef, inputRef,
color, color,
containerRef,
}: DraggableTextInputProps) => { }: DraggableTextInputProps) => {
const [isDragging, setIsDragging] = useState(false); const [isDragging, setIsDragging] = useState(false);
const dragOffset = useRef({ x: 0, y: 0 }); const dragOffset = useRef({ x: 0, y: 0 });
const elementRef = useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
const handleMouseMove = (e: MouseEvent) => { const handleMouseMove = (e: MouseEvent) => {
if (isDragging) { e.preventDefault();
const newX = e.clientX - dragOffset.current.x; e.stopPropagation();
const newY = e.clientY - dragOffset.current.y; if (isDragging && containerRef?.current && elementRef.current) {
const containerRect = containerRef.current.getBoundingClientRect();
const elementRect = elementRef.current.getBoundingClientRect();
let newX = e.clientX - dragOffset.current.x;
let newY = e.clientY - dragOffset.current.y;
// Constrain within container bounds
newX = Math.max(
0,
Math.min(newX, containerRect.width - elementRect.width),
);
newY = Math.max(
0,
Math.min(newY, containerRect.height - elementRect.height),
);
// Calculate adjusted coordinates for the canvas // Calculate adjusted coordinates for the canvas
const adjustedX = newX / scale; const adjustedX = newX / scale;
const adjustedY = newY / scale; const adjustedY = newY / scale;
@@ -69,10 +88,11 @@ export const DraggableTextInput = ({
document.removeEventListener("mouseup", handleMouseUp); document.removeEventListener("mouseup", handleMouseUp);
}; };
} }
}, [isDragging, input.id, onMove, scale]); }, [isDragging, input.id, onMove, scale, containerRef]);
return ( return (
<div <div
ref={elementRef}
className="absolute z-[999]" className="absolute z-[999]"
style={{ style={{
left: `${input.x}px`, left: `${input.x}px`,

View File

@@ -377,6 +377,7 @@ export const Annotator = ({
spanRef={spanRef} spanRef={spanRef}
inputRef={inputRef} inputRef={inputRef}
color={input.color} color={input.color}
containerRef={containerRef}
/> />
))} ))}