UNPKG

@jbrowse/plugin-linear-genome-view

Version:

JBrowse 2 linear genome view

159 lines (158 loc) 5.12 kB
import { useCallback, useEffect, useState } from 'react'; import { getRelativeX } from "./util.js"; export function useRangeSelect(ref, model, shiftOnly) { const [startX, setStartX] = useState(); const [currentX, setCurrentX] = useState(); const [anchorPosition, setAnchorPosition] = useState(); const [guideX, setGuideX] = useState(); const mouseDragging = startX !== undefined && anchorPosition === undefined; const handleClose = useCallback(() => { setAnchorPosition(undefined); setStartX(undefined); setCurrentX(undefined); setGuideX(undefined); }, []); useEffect(() => { function computeOffsets(offsetX) { if (startX === undefined) { return; } const leftPx = Math.min(startX, offsetX); const rightPx = Math.max(startX, offsetX); return { leftOffset: model.pxToBp(leftPx), rightOffset: model.pxToBp(rightPx), }; } function globalMouseMove(event) { if (ref.current && mouseDragging) { const relativeX = getRelativeX(event, ref.current); setCurrentX(relativeX); } } function globalMouseUp(event) { if (startX !== undefined && ref.current) { const { clientX, clientY } = event; const offsetX = getRelativeX(event, ref.current); const isClick = Math.abs(offsetX - startX) <= 3; if (isClick && model.scalebarRefNameClickPending) { setStartX(undefined); setCurrentX(undefined); return; } if (!isClick && model.scalebarRefNameClickPending) { model.setScalebarRefNameClickPending(false); } setAnchorPosition({ offsetX, clientX, clientY, isClick, }); if (isClick) { setGuideX(offsetX); } else { const args = computeOffsets(offsetX); if (args) { model.setOffsets(args.leftOffset, args.rightOffset); } setGuideX(undefined); } } } if (mouseDragging) { window.addEventListener('mousemove', globalMouseMove); window.addEventListener('mouseup', globalMouseUp); return () => { window.removeEventListener('mousemove', globalMouseMove); window.removeEventListener('mouseup', globalMouseUp); }; } return () => { }; }, [startX, mouseDragging, model, ref, handleClose]); function mouseDown(event) { if (shiftOnly && !event.shiftKey) { return; } if (model.isScalebarRefNameMenuOpen) { return; } event.preventDefault(); event.stopPropagation(); const relativeX = getRelativeX(event, ref.current); setStartX(relativeX); setCurrentX(relativeX); } function mouseMove(event) { if (anchorPosition?.isClick === false) { return; } if (mouseDragging) { setGuideX(undefined); } else if (shiftOnly) { if (event.shiftKey) { setGuideX(getRelativeX(event, ref.current)); } else { setGuideX(undefined); } } else { setGuideX(getRelativeX(event, ref.current)); } } function mouseOut() { if (!anchorPosition?.isClick) { setGuideX(undefined); } } function handleMenuItemClick(_, callback) { callback(); handleClose(); } const open = Boolean(anchorPosition); const isClick = anchorPosition?.isClick; const clickBpOffset = isClick ? model.pxToBp(anchorPosition.offsetX) : undefined; if (startX === undefined) { return { open, isClick, clickBpOffset, guideX, mouseDown, mouseMove, mouseOut, handleClose, handleMenuItemClick, anchorPosition, }; } const right = anchorPosition ? anchorPosition.offsetX : currentX || 0; const left = Math.min(right, startX); const width = Math.abs(right - startX); const leftBpOffset = model.pxToBp(left); const rightBpOffset = model.pxToBp(left + width); const numOfBpSelected = Math.ceil(width * model.bpPerPx); return { open, isClick, clickBpOffset, guideX, rubberbandOn: !isClick, mouseDown, mouseMove, mouseOut, handleClose, handleMenuItemClick, leftBpOffset, rightBpOffset, anchorPosition, numOfBpSelected, width, left, }; }