web click n drag
This commit is contained in:
@@ -171,6 +171,15 @@ export function Gallery({
|
|||||||
horizontal
|
horizontal
|
||||||
pagingEnabled={false}
|
pagingEnabled={false}
|
||||||
showsHorizontalScrollIndicator={false}
|
showsHorizontalScrollIndicator={false}
|
||||||
|
snapToOffsets={images.reduce<number[]>((offsets, image, i) => {
|
||||||
|
const prev =
|
||||||
|
i === 0
|
||||||
|
? 0
|
||||||
|
: offsets[i - 1] + getItemWidth(images[i - 1]) + ITEM_GAP
|
||||||
|
offsets.push(prev)
|
||||||
|
return offsets
|
||||||
|
}, [])}
|
||||||
|
snapToAlignment="start"
|
||||||
decelerationRate="normal"
|
decelerationRate="normal"
|
||||||
style={{
|
style={{
|
||||||
width: bleed ? windowWidth : containerWidth + QUOTE_PADDING * 2,
|
width: bleed ? windowWidth : containerWidth + QUOTE_PADDING * 2,
|
||||||
|
|||||||
@@ -100,6 +100,60 @@ export function Gallery({
|
|||||||
}
|
}
|
||||||
const currentScrollX = useRef(0)
|
const currentScrollX = useRef(0)
|
||||||
|
|
||||||
|
// Click-and-drag scrolling
|
||||||
|
const isDragging = useRef(false)
|
||||||
|
const dragStartX = useRef(0)
|
||||||
|
const dragScrollStart = useRef(0)
|
||||||
|
const hasDragged = useRef(false)
|
||||||
|
const scrollNodeRef = useRef<HTMLElement | null>(null)
|
||||||
|
|
||||||
|
const onMouseDown = (e: React.MouseEvent) => {
|
||||||
|
const node = scrollNodeRef.current
|
||||||
|
if (!node || e.button !== 0) return
|
||||||
|
isDragging.current = true
|
||||||
|
hasDragged.current = false
|
||||||
|
dragStartX.current = e.clientX
|
||||||
|
dragScrollStart.current = node.scrollLeft
|
||||||
|
node.style.scrollBehavior = 'auto'
|
||||||
|
node.style.scrollSnapType = 'none'
|
||||||
|
node.style.cursor = 'grabbing'
|
||||||
|
node.style.userSelect = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
const onMouseMove = (e: React.MouseEvent) => {
|
||||||
|
if (!isDragging.current) return
|
||||||
|
const node = scrollNodeRef.current
|
||||||
|
if (!node) return
|
||||||
|
const dx = e.clientX - dragStartX.current
|
||||||
|
if (Math.abs(dx) > 3) {
|
||||||
|
hasDragged.current = true
|
||||||
|
}
|
||||||
|
node.scrollLeft = dragScrollStart.current - dx
|
||||||
|
}
|
||||||
|
|
||||||
|
const onMouseUp = () => {
|
||||||
|
if (!isDragging.current) return
|
||||||
|
isDragging.current = false
|
||||||
|
const node = scrollNodeRef.current
|
||||||
|
if (!node) return
|
||||||
|
node.style.scrollBehavior = 'smooth'
|
||||||
|
node.style.scrollSnapType = 'x proximity'
|
||||||
|
node.style.cursor = ''
|
||||||
|
node.style.userSelect = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const onScrollViewRef = (ref: ScrollView | null) => {
|
||||||
|
scrollRef.current = ref
|
||||||
|
// Get the underlying DOM scroll node from the RN web ScrollView
|
||||||
|
if (ref) {
|
||||||
|
const scrollable = ref as unknown as {
|
||||||
|
getScrollableNode?: () => HTMLElement
|
||||||
|
}
|
||||||
|
scrollNodeRef.current =
|
||||||
|
scrollable.getScrollableNode?.() ?? (ref as unknown as HTMLElement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={
|
style={
|
||||||
@@ -121,10 +175,15 @@ export function Gallery({
|
|||||||
aria-label={_(msg`Image gallery, ${images.length} images`)}>
|
aria-label={_(msg`Image gallery, ${images.length} images`)}>
|
||||||
{containerWidth > 0 && (
|
{containerWidth > 0 && (
|
||||||
<ScrollView
|
<ScrollView
|
||||||
ref={scrollRef}
|
ref={onScrollViewRef}
|
||||||
horizontal
|
horizontal
|
||||||
showsHorizontalScrollIndicator={false}
|
showsHorizontalScrollIndicator={false}
|
||||||
scrollEventThrottle={16}
|
scrollEventThrottle={16}
|
||||||
|
// @ts-expect-error web mouse events
|
||||||
|
onMouseDown={onMouseDown}
|
||||||
|
onMouseMove={onMouseMove}
|
||||||
|
onMouseUp={onMouseUp}
|
||||||
|
onMouseLeave={onMouseUp}
|
||||||
style={[
|
style={[
|
||||||
{
|
{
|
||||||
width: bleed ? windowWidth : containerWidth + QUOTE_PADDING * 2,
|
width: bleed ? windowWidth : containerWidth + QUOTE_PADDING * 2,
|
||||||
@@ -132,8 +191,10 @@ export function Gallery({
|
|||||||
marginLeft: -insetLeft,
|
marginLeft: -insetLeft,
|
||||||
},
|
},
|
||||||
web({
|
web({
|
||||||
|
scrollSnapType: 'x proximity',
|
||||||
scrollBehavior: 'smooth',
|
scrollBehavior: 'smooth',
|
||||||
WebkitOverflowScrolling: 'touch',
|
WebkitOverflowScrolling: 'touch',
|
||||||
|
cursor: 'grab',
|
||||||
}),
|
}),
|
||||||
]}
|
]}
|
||||||
contentContainerStyle={{
|
contentContainerStyle={{
|
||||||
@@ -174,6 +235,7 @@ export function Gallery({
|
|||||||
width: getItemWidth(image),
|
width: getItemWidth(image),
|
||||||
height: containerHeight,
|
height: containerHeight,
|
||||||
},
|
},
|
||||||
|
web({scrollSnapAlign: 'start'}),
|
||||||
]}
|
]}
|
||||||
aria-roledescription="slide"
|
aria-roledescription="slide"
|
||||||
aria-label={
|
aria-label={
|
||||||
@@ -183,6 +245,7 @@ export function Gallery({
|
|||||||
onPress={
|
onPress={
|
||||||
onPress
|
onPress
|
||||||
? () => {
|
? () => {
|
||||||
|
if (hasDragged.current) return
|
||||||
ax.metric('post:gallery:openLightbox', {
|
ax.metric('post:gallery:openLightbox', {
|
||||||
imageIndex: index,
|
imageIndex: index,
|
||||||
totalImages: images.length,
|
totalImages: images.length,
|
||||||
|
|||||||
Reference in New Issue
Block a user