Skip to content

Instantly share code, notes, and snippets.

@alfonsusac
Last active March 7, 2024 15:08
Show Gist options
  • Save alfonsusac/e699034b8f6ef1085600e19c24f89bb5 to your computer and use it in GitHub Desktop.
Save alfonsusac/e699034b8f6ef1085600e19c24f89bb5 to your computer and use it in GitHub Desktop.
React useMouseEventListener -> useMouse -> useMouseDrag
// Usage
export function Model(
props: {
data: NodeData
onDragEnd: (newPos: Pos) => void
}
) {
const [position, setPosition] = useState(props.data.position)
const ref = useRef<HTMLDivElement>(null)
const {
dragging,
} = useMouseDrag(
ref,
new Pos(0,0),
(delta) => {
setPosition(position.add(delta))
},
() => {
props.onDragEnd(position)
}
)
return <div
id={props.data.id}
ref={ref}
className="w-40 h-40 bg-orange-300 select-none shadow-xl"
style={{
transform: `translateX(${ position.x }px) translateY(${ position.y }px)`
}}
>
{position + ""}<br />
{dragging + ""}<br />
</div>
}
// #1
export function useMouseEventListener(
mouseEv: (data: {
position: Pos,
scroll: number,
leftClick: boolean,
rightClick: boolean,
middleClick: boolean,
}) => void
) {
useEffect(() => {
function mouseEventHandler(e: MouseEvent) {
// console.log(e.button, e.buttons)
mouseEv({
position: new Pos(e.clientX, e.clientY),
scroll: 0,
leftClick: e.buttons === 1,
rightClick: e.buttons === 2,
middleClick: e.buttons === 4,
})
}
function wheelEventHandler(e: WheelEvent) {
// console.log(e.button, e.buttons)
mouseEv({
position: new Pos(e.clientX, e.clientY),
scroll: e.deltaZ,
leftClick: e.buttons === 1,
rightClick: e.buttons === 2,
middleClick: e.buttons === 4,
})
}
window.addEventListener('mousemove', mouseEventHandler)
window.addEventListener('mousedown', mouseEventHandler)
window.addEventListener('mouseup', mouseEventHandler)
window.addEventListener('wheel', wheelEventHandler)
return () => {
window.removeEventListener('mousemove', mouseEventHandler)
window.removeEventListener('mousedown', mouseEventHandler)
window.removeEventListener('mouseup', mouseEventHandler)
window.removeEventListener('wheel', wheelEventHandler)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
}
// #2
export function useMouse(mouseEv: (
data: CustomMouseEventPayload) => void,
) {
const [mouseBasicEvent, setMouseBasicEvent] = useState<{
position: Pos,
scroll: number,
leftClick: boolean,
rightClick: boolean,
middleClick: boolean,
}>()
const [prevPosition, setPrevPosition] = useState<null | Pos>(null)
const [positionDelta, setPositionDelta] = useState(new Pos(0, 0))
useMouseEventListener((event) => {
setMouseBasicEvent(event)
})
useEffect(() => {
if (mouseBasicEvent) {
const delta = mouseBasicEvent.position.subtract(prevPosition ?? mouseBasicEvent.position)
setPrevPosition(mouseBasicEvent.position)
setPositionDelta(delta)
// toast(delta + '')
mouseEv({
...mouseBasicEvent,
positionDelta: delta,
})
}
}, [mouseBasicEvent])
return {
...mouseBasicEvent,
positionDelta
}
}
// #3
export function useMouseDrag<T extends HTMLElement>(
target: RefObject<T>,
initialPosition: Pos, // todo: remove this
onDrag: (delta: Pos) => void,
onEnd: () => void,
scale: number = 1,
) {
const [dragging, setDragging] = useState(false)
const { dragRef, setDragRef } = useContext(GlobalDragContext)
const mouse = useMouse(() => { })
const {
leftClick,
positionDelta,
position: mousePos,
} = mouse
useEffect(() => {
if (leftClick && !positionDelta.isZero && !dragging && mousePos && !dragRef) {
const el = document.elementFromPoint(mousePos.x, mousePos.y)
if (!el) return
if (!target.current) return
if (el.id === target.current.id) {
setDragging(true)
setDragRef(el.id)
}
}
}, [leftClick, positionDelta, dragging, mousePos, target, dragRef, setDragRef])
useEffect(() => {
if (leftClick === false && dragging) {
setDragging(false)
setDragRef(null)
onEnd()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [leftClick, dragging, dragRef, setDragRef])
useEffect(() => {
if (dragging && positionDelta) {
onDrag(positionDelta)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dragging, positionDelta])
return {
dragging,
...mouse,
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment