From 3da25b93c5bfa1f0e7123ab0e40bd27088306ece Mon Sep 17 00:00:00 2001 From: nnhhoang Date: Thu, 26 Mar 2026 13:31:38 +0700 Subject: [PATCH] fix: require click to activate scroll-to-adjust on sliders Previously, hovering over any SliderControl would hijack the scroll wheel, causing unintentional value changes when scrolling through property panels (e.g. door, window). Now the slider must be clicked to focus before scroll adjusts its value. Click outside or press Escape to unfocus. Closes #161 --- .../components/ui/controls/slider-control.tsx | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/packages/editor/src/components/ui/controls/slider-control.tsx b/packages/editor/src/components/ui/controls/slider-control.tsx index d4e18f82..0b325a5c 100644 --- a/packages/editor/src/components/ui/controls/slider-control.tsx +++ b/packages/editor/src/components/ui/controls/slider-control.tsx @@ -29,7 +29,7 @@ export function SliderControl({ }: SliderControlProps) { const [isEditing, setIsEditing] = useState(false) const [isDragging, setIsDragging] = useState(false) - const [isHovered, setIsHovered] = useState(false) + const [isFocused, setIsFocused] = useState(false) const [inputValue, setInputValue] = useState(value.toFixed(precision)) // Track the original value and bounds when dragging starts @@ -58,7 +58,7 @@ export function SliderControl({ useEffect(() => { const container = containerRef.current - if (!container) return + if (!container || !isFocused) return const handleWheel = (e: WheelEvent) => { if (isEditing) return @@ -80,12 +80,26 @@ export function SliderControl({ container.addEventListener('wheel', handleWheel, { passive: false }) return () => container.removeEventListener('wheel', handleWheel) - }, [isEditing, step, clamp, onChange, precision]) + }, [isFocused, isEditing, step, clamp, onChange, precision]) + // Unfocus when clicking outside or pressing Escape useEffect(() => { - if (!isHovered || isEditing) return + if (!isFocused) return + + const handlePointerDownOutside = (e: PointerEvent) => { + if (containerRef.current && !containerRef.current.contains(e.target as Node)) { + setIsFocused(false) + } + } const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + setIsFocused(false) + return + } + + if (isEditing) return + let direction = 0 if (e.key === 'ArrowUp') direction = 1 else if (e.key === 'ArrowDown') direction = -1 @@ -105,9 +119,13 @@ export function SliderControl({ } } + window.addEventListener('pointerdown', handlePointerDownOutside) window.addEventListener('keydown', handleKeyDown) - return () => window.removeEventListener('keydown', handleKeyDown) - }, [isHovered, isEditing, step, clamp, onChange, precision]) + return () => { + window.removeEventListener('pointerdown', handlePointerDownOutside) + window.removeEventListener('keydown', handleKeyDown) + } + }, [isFocused, isEditing, step, clamp, onChange, precision]) const handlePointerDown = useCallback( (e: React.PointerEvent) => { @@ -238,12 +256,15 @@ export function SliderControl({ return (
setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} + onClick={() => setIsFocused(true)} ref={containerRef} > {/* Reset button that appears when dragged away from start */}