diff options
author | Paul Frazee <pfrazee@gmail.com> | 2023-05-24 18:46:55 -0500 |
---|---|---|
committer | Paul Frazee <pfrazee@gmail.com> | 2023-05-24 18:46:55 -0500 |
commit | 0a18229784ea6c67e5ac17e4bae1a79a24f80d7f (patch) | |
tree | af829bdacd5489e9e012b74a3778752ed3efbce0 /src/lib/hooks/useDraggableScrollView.ts | |
parent | 4e1876fe85ab3a70eba50466a62bff8a9d01c16c (diff) | |
parent | 32c9dabb7467149baf39d8f5c2eb3d0b81236d92 (diff) | |
download | voidsky-0a18229784ea6c67e5ac17e4bae1a79a24f80d7f.tar.zst |
Merge branch 'custom-algos' of github.com:bluesky-social/social-app into custom-algos
Diffstat (limited to 'src/lib/hooks/useDraggableScrollView.ts')
-rw-r--r-- | src/lib/hooks/useDraggableScrollView.ts | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/src/lib/hooks/useDraggableScrollView.ts b/src/lib/hooks/useDraggableScrollView.ts new file mode 100644 index 000000000..b0f7465d7 --- /dev/null +++ b/src/lib/hooks/useDraggableScrollView.ts @@ -0,0 +1,84 @@ +import {useEffect, useRef, useMemo, ForwardedRef} from 'react' +import {Platform, findNodeHandle} from 'react-native' +import type {ScrollView} from 'react-native' +import {mergeRefs} from 'lib/merge-refs' + +type Props<Scrollable extends ScrollView = ScrollView> = { + cursor?: string + outerRef?: ForwardedRef<Scrollable> +} + +export function useDraggableScroll<Scrollable extends ScrollView = ScrollView>({ + outerRef, + cursor = 'grab', +}: Props<Scrollable> = {}) { + const ref = useRef<Scrollable>(null) + + useEffect(() => { + if (Platform.OS !== 'web' || !ref.current) { + return + } + const slider = findNodeHandle(ref.current) as unknown as HTMLDivElement + if (!slider) { + return + } + let isDragging = false + let isMouseDown = false + let startX = 0 + let scrollLeft = 0 + + const mouseDown = (e: MouseEvent) => { + isMouseDown = true + startX = e.pageX - slider.offsetLeft + scrollLeft = slider.scrollLeft + + slider.style.cursor = cursor + } + + const mouseUp = () => { + if (isDragging) { + slider.addEventListener('click', e => e.stopPropagation(), {once: true}) + } + + isMouseDown = false + isDragging = false + slider.style.cursor = 'default' + } + + const mouseMove = (e: MouseEvent) => { + if (!isMouseDown) { + return + } + + // Require n pixels momement before start of drag (3 in this case ) + const x = e.pageX - slider.offsetLeft + if (Math.abs(x - startX) < 3) { + return + } + + isDragging = true + e.preventDefault() + const walk = x - startX + slider.scrollLeft = scrollLeft - walk + } + + slider.addEventListener('mousedown', mouseDown) + window.addEventListener('mouseup', mouseUp) + window.addEventListener('mousemove', mouseMove) + + return () => { + slider.removeEventListener('mousedown', mouseDown) + window.removeEventListener('mouseup', mouseUp) + window.removeEventListener('mousemove', mouseMove) + } + }, [cursor]) + + const refs = useMemo( + () => mergeRefs(outerRef ? [ref, outerRef] : [ref]), + [ref, outerRef], + ) + + return { + refs, + } +} |