Use same scroll handling for both web hooks
This commit is contained in:
@@ -30,6 +30,7 @@ import {BlockDrawerGesture} from '#/view/shell/BlockDrawerGesture'
|
||||
import {useKeyboardHandlers} from '#/components/images/Gallery/useKeyboardHandlers'
|
||||
import {usePointerHandlers} from '#/components/images/Gallery/usePointerHandlers'
|
||||
import {CONTAINER_ASPECT_RATIO, ITEM_GAP, MIN_PEEK} from '#/components/images/Gallery/const'
|
||||
import {IS_WEB} from '#/env'
|
||||
|
||||
interface GalleryProps {
|
||||
images: AppBskyEmbedImages.ViewImage[]
|
||||
@@ -55,6 +56,7 @@ export function GalleryBleed({children}: {children: React.ReactNode}) {
|
||||
const [bleedWidth, setBleedWidth] = useState(0)
|
||||
|
||||
if (!isValidElement(children)) {
|
||||
|
||||
throw new Error('GalleryBleed children must be a single React element')
|
||||
}
|
||||
|
||||
@@ -132,22 +134,22 @@ export function Gallery({
|
||||
const itemRefsRef = useRef<Map<number, View>>(new Map())
|
||||
const currentIndexRef = useRef(0)
|
||||
|
||||
const scrollToIndex = (index: number, animated = true) => {
|
||||
let offset = 0
|
||||
for (let i = 0; i < index; i++) {
|
||||
offset += (itemWidthsRef.current.get(i) ?? 0) + ITEM_GAP
|
||||
}
|
||||
flatListRef.current?.scrollToOffset({offset, animated})
|
||||
const scrollTo = (offset: number) => {
|
||||
flatListRef.current?.scrollToOffset({offset, animated: false})
|
||||
}
|
||||
|
||||
const onSettle = (index: number) => {
|
||||
if (!IS_WEB) return
|
||||
const el = itemRefsRef.current.get(index) as unknown as HTMLElement | null
|
||||
el?.focus({preventScroll: true})
|
||||
}
|
||||
|
||||
useKeyboardHandlers({
|
||||
flatListRef,
|
||||
itemWidthsRef,
|
||||
currentIndexRef,
|
||||
scrollToIndex(index: number) {
|
||||
scrollToIndex(index)
|
||||
const el = itemRefsRef.current.get(index) as unknown as HTMLElement | null
|
||||
el?.focus({preventScroll: true})
|
||||
},
|
||||
scrollTo,
|
||||
onSettle,
|
||||
imageCount: images.length,
|
||||
})
|
||||
|
||||
@@ -155,10 +157,8 @@ export function Gallery({
|
||||
flatListRef,
|
||||
itemWidthsRef,
|
||||
currentIndexRef,
|
||||
onSettle(index: number) {
|
||||
const el = itemRefsRef.current.get(index) as unknown as HTMLElement | null
|
||||
el?.focus({preventScroll: true})
|
||||
},
|
||||
scrollTo,
|
||||
onSettle,
|
||||
imageCount: images.length,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
export function useKeyboardHandlers(_args: {
|
||||
flatListRef: any
|
||||
itemWidthsRef: any
|
||||
currentIndexRef: any
|
||||
scrollToIndex: any
|
||||
scrollTo: any
|
||||
onSettle: any
|
||||
imageCount: any
|
||||
}) {}
|
||||
|
||||
@@ -1,43 +1,89 @@
|
||||
import {useEffect} from 'react'
|
||||
import {type FlatList} from 'react-native'
|
||||
|
||||
import {ITEM_GAP} from '#/components/images/Gallery/const'
|
||||
import {tween} from '#/components/images/Gallery/tween'
|
||||
|
||||
const SETTLE_DURATION = 600
|
||||
|
||||
function getOffsetForIndex(
|
||||
itemWidths: Map<number, number>,
|
||||
index: number,
|
||||
): number {
|
||||
let offset = 0
|
||||
for (let i = 0; i < index; i++) {
|
||||
offset += (itemWidths.get(i) ?? 0) + ITEM_GAP
|
||||
}
|
||||
return offset
|
||||
}
|
||||
|
||||
export function useKeyboardHandlers({
|
||||
flatListRef,
|
||||
itemWidthsRef,
|
||||
currentIndexRef,
|
||||
scrollToIndex,
|
||||
scrollTo,
|
||||
onSettle,
|
||||
imageCount,
|
||||
}: {
|
||||
flatListRef: React.RefObject<FlatList | null>
|
||||
itemWidthsRef: React.RefObject<Map<number, number>>
|
||||
currentIndexRef: React.RefObject<number>
|
||||
scrollToIndex: (index: number, animated?: boolean) => void
|
||||
scrollTo: (offset: number) => void
|
||||
onSettle: (index: number) => void
|
||||
imageCount: number
|
||||
}) {
|
||||
useEffect(() => {
|
||||
if (imageCount <= 1) return
|
||||
|
||||
let stopTween: (() => void) | null = null
|
||||
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
const el = flatListRef.current?.getScrollableNode() as unknown as HTMLElement | null
|
||||
const el =
|
||||
flatListRef.current?.getScrollableNode() as unknown as HTMLElement | null
|
||||
if (!el || !el.contains(document.activeElement)) return
|
||||
|
||||
const current = currentIndexRef.current
|
||||
let targetIndex: number | undefined
|
||||
|
||||
if (e.key === 'ArrowRight' || (e.key === 'Tab' && !e.shiftKey)) {
|
||||
const next = current + 1
|
||||
if (next < imageCount) {
|
||||
e.preventDefault()
|
||||
scrollToIndex(next)
|
||||
currentIndexRef.current = next
|
||||
if (current < imageCount - 1) {
|
||||
targetIndex = current + 1
|
||||
}
|
||||
} else if (e.key === 'ArrowLeft' || (e.key === 'Tab' && e.shiftKey)) {
|
||||
const prev = current - 1
|
||||
if (prev >= 0) {
|
||||
e.preventDefault()
|
||||
scrollToIndex(prev)
|
||||
currentIndexRef.current = prev
|
||||
if (current > 0) {
|
||||
targetIndex = current - 1
|
||||
}
|
||||
}
|
||||
|
||||
if (targetIndex != null) {
|
||||
e.preventDefault()
|
||||
|
||||
if (stopTween) {
|
||||
stopTween()
|
||||
stopTween = null
|
||||
}
|
||||
|
||||
const from = el.scrollLeft
|
||||
const to = getOffsetForIndex(itemWidthsRef.current, targetIndex)
|
||||
const idx = targetIndex
|
||||
|
||||
stopTween = tween(from, to, SETTLE_DURATION)(
|
||||
v => {
|
||||
scrollTo(v)
|
||||
},
|
||||
() => {
|
||||
stopTween = null
|
||||
currentIndexRef.current = idx
|
||||
onSettle(idx)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', onKeyDown)
|
||||
return () => window.removeEventListener('keydown', onKeyDown)
|
||||
}, [flatListRef, currentIndexRef, scrollToIndex, imageCount])
|
||||
return () => {
|
||||
window.removeEventListener('keydown', onKeyDown)
|
||||
if (stopTween) stopTween()
|
||||
}
|
||||
}, [flatListRef, itemWidthsRef, currentIndexRef, scrollTo, onSettle, imageCount])
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ export function usePointerHandlers(_args: {
|
||||
flatListRef: any
|
||||
itemWidthsRef: any
|
||||
currentIndexRef: any
|
||||
scrollTo: any
|
||||
onSettle: any
|
||||
imageCount: any
|
||||
}) {}
|
||||
|
||||
@@ -51,12 +51,14 @@ export function usePointerHandlers({
|
||||
flatListRef,
|
||||
itemWidthsRef,
|
||||
currentIndexRef,
|
||||
scrollTo,
|
||||
onSettle,
|
||||
imageCount,
|
||||
}: {
|
||||
flatListRef: React.RefObject<FlatList | null>
|
||||
itemWidthsRef: React.RefObject<Map<number, number>>
|
||||
currentIndexRef: React.RefObject<number>
|
||||
scrollTo: (offset: number) => void
|
||||
onSettle: (index: number) => void
|
||||
imageCount: number
|
||||
}) {
|
||||
@@ -126,10 +128,10 @@ export function usePointerHandlers({
|
||||
velo = (delta - prevDelta) / (elapsed * FRAME_MS)
|
||||
t = e.timeStamp
|
||||
|
||||
el.scrollLeft = dragScrollLeft - delta
|
||||
scrollTo(dragScrollLeft - delta)
|
||||
|
||||
// Update current index from scroll position
|
||||
const offsetX = el.scrollLeft
|
||||
const offsetX = dragScrollLeft - delta
|
||||
let accumulated = 0
|
||||
for (let i = 0; i < imageCount; i++) {
|
||||
const w = (itemWidthsRef.current.get(i) ?? 0) + ITEM_GAP
|
||||
@@ -184,7 +186,7 @@ export function usePointerHandlers({
|
||||
|
||||
stopTween = tween(from, to, SETTLE_DURATION)(
|
||||
v => {
|
||||
el.scrollLeft = v
|
||||
scrollTo(v)
|
||||
},
|
||||
() => {
|
||||
stopTween = null
|
||||
@@ -207,5 +209,5 @@ export function usePointerHandlers({
|
||||
el.style.cursor = ''
|
||||
el.style.userSelect = ''
|
||||
}
|
||||
}, [flatListRef, itemWidthsRef, currentIndexRef, onSettle, imageCount])
|
||||
}, [flatListRef, itemWidthsRef, currentIndexRef, scrollTo, onSettle, imageCount])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user