mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-10 04:30:25 +02:00
add horizontal scroll to feature carousel (#1123)
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import type { MouseEvent } from 'react';
|
import type { MouseEvent } from 'react';
|
||||||
|
|
||||||
import { AnimatePresence, motion } from 'motion/react';
|
import { AnimatePresence, motion } from 'motion/react';
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { generatePath, Link } from 'react-router';
|
import { generatePath, Link } from 'react-router';
|
||||||
|
|
||||||
import styles from './feature-carousel.module.css';
|
import styles from './feature-carousel.module.css';
|
||||||
@@ -209,28 +209,70 @@ export const FeatureCarousel = ({ data, onNearEnd }: FeatureCarouselProps) => {
|
|||||||
}
|
}
|
||||||
}, [data, startIndex, itemsPerRow, onNearEnd]);
|
}, [data, startIndex, itemsPerRow, onNearEnd]);
|
||||||
|
|
||||||
const handleNext = (e?: MouseEvent<HTMLButtonElement>) => {
|
const handleNext = useCallback(
|
||||||
e?.preventDefault();
|
(e?: MouseEvent<HTMLButtonElement>) => {
|
||||||
e?.stopPropagation();
|
e?.preventDefault();
|
||||||
if (!data) return;
|
e?.stopPropagation();
|
||||||
directionRef.current = { isNext: true };
|
if (!data) return;
|
||||||
setStartIndex((prev) => (prev + itemsPerRow) % data.length);
|
directionRef.current = { isNext: true };
|
||||||
};
|
setStartIndex((prev) => (prev + itemsPerRow) % data.length);
|
||||||
|
},
|
||||||
|
[data, itemsPerRow],
|
||||||
|
);
|
||||||
|
|
||||||
const handlePrevious = (e?: MouseEvent<HTMLButtonElement>) => {
|
const handlePrevious = useCallback(
|
||||||
e?.preventDefault();
|
(e?: MouseEvent<HTMLButtonElement>) => {
|
||||||
e?.stopPropagation();
|
e?.preventDefault();
|
||||||
if (!data) return;
|
e?.stopPropagation();
|
||||||
directionRef.current = { isNext: false };
|
if (!data) return;
|
||||||
setStartIndex((prev) => (prev - itemsPerRow + data.length) % data.length);
|
directionRef.current = { isNext: false };
|
||||||
};
|
setStartIndex((prev) => (prev - itemsPerRow + data.length) % data.length);
|
||||||
|
},
|
||||||
|
[data, itemsPerRow],
|
||||||
|
);
|
||||||
|
|
||||||
|
const canNavigate = data && data.length > itemsPerRow;
|
||||||
|
|
||||||
|
const wheelCooldownRef = useRef(0);
|
||||||
|
const wheelThreshold = 10;
|
||||||
|
const wheelCooldownMs = 250;
|
||||||
|
|
||||||
|
const handleWheel = useCallback(
|
||||||
|
(event: React.WheelEvent<HTMLDivElement>) => {
|
||||||
|
if (!canNavigate || !data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!event.shiftKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
const elapsed = now - wheelCooldownRef.current;
|
||||||
|
|
||||||
|
const horizontalDelta = Math.abs(event.deltaY);
|
||||||
|
|
||||||
|
if (horizontalDelta < wheelThreshold || elapsed < wheelCooldownMs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.deltaY > 0) {
|
||||||
|
wheelCooldownRef.current = now;
|
||||||
|
handleNext();
|
||||||
|
} else if (event.deltaY < 0) {
|
||||||
|
wheelCooldownRef.current = now;
|
||||||
|
handlePrevious();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[canNavigate, data, handleNext, handlePrevious, wheelCooldownMs, wheelThreshold],
|
||||||
|
);
|
||||||
|
|
||||||
if (!data || data.length === 0) {
|
if (!data || data.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.carouselContainer} ref={containerRef}>
|
<div className={styles.carouselContainer} onWheel={handleWheel} ref={containerRef}>
|
||||||
<AnimatePresence initial={false} mode="popLayout">
|
<AnimatePresence initial={false} mode="popLayout">
|
||||||
<motion.div
|
<motion.div
|
||||||
animate="animate"
|
animate="animate"
|
||||||
|
|||||||
Reference in New Issue
Block a user