You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
55 lines
1.3 KiB
55 lines
1.3 KiB
import { useRef, useCallback } from 'react'; |
|
|
|
type Position = [number, number]; |
|
|
|
export const useSelectableClick = ( |
|
onClick: React.MouseEventHandler, |
|
maxDelta = 5, |
|
) => { |
|
const clickPositionRef = useRef<Position | null>(null); |
|
|
|
const handleMouseDown = useCallback((e: React.MouseEvent) => { |
|
clickPositionRef.current = [e.clientX, e.clientY]; |
|
}, []); |
|
|
|
const handleMouseUp = useCallback( |
|
(e: React.MouseEvent) => { |
|
if (!clickPositionRef.current) { |
|
return; |
|
} |
|
|
|
const [startX, startY] = clickPositionRef.current; |
|
const [deltaX, deltaY] = [ |
|
Math.abs(e.clientX - startX), |
|
Math.abs(e.clientY - startY), |
|
]; |
|
|
|
let element: EventTarget | null = e.target; |
|
|
|
while (element && element instanceof HTMLElement) { |
|
if ( |
|
element.localName === 'button' || |
|
element.localName === 'a' || |
|
element.localName === 'label' |
|
) { |
|
return; |
|
} |
|
|
|
element = element.parentNode; |
|
} |
|
|
|
if ( |
|
deltaX + deltaY < maxDelta && |
|
(e.button === 0 || e.button === 1) && |
|
e.detail >= 1 |
|
) { |
|
onClick(e); |
|
} |
|
|
|
clickPositionRef.current = null; |
|
}, |
|
[maxDelta, onClick], |
|
); |
|
|
|
return [handleMouseDown, handleMouseUp] as const; |
|
};
|
|
|