fix(explore): support mouse-wheel scrolling on desktop
Hiding the scrollbar (previous commit) removed the only desktop affordance for moving a horizontally-scrolling, scrollbar-hidden row — mouse wheel scrolls vertically by default and there's no drag handle anymore. Adds useHorizontalScroll, which redirects vertical wheel delta to scrollLeft while leaving trackpad horizontal gestures and touch swipe untouched. Applied to the explore species tabs and StoryTray, which had the identical latent issue.
This commit is contained in:
@@ -7,6 +7,7 @@ import { useInfiniteQuery, useQuery } from "@tanstack/react-query";
|
||||
import { Loader2, TrendingUp } from "lucide-react";
|
||||
import { useTRPC } from "@/trpc/client";
|
||||
import { useActivePet } from "@/context/ActivePetContext";
|
||||
import { useHorizontalScroll } from "@/hooks/use-horizontal-scroll";
|
||||
import { ExploreCard } from "@/components/explore/ExploreCard";
|
||||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
@@ -32,6 +33,7 @@ export default function ExplorePage() {
|
||||
const [activeTab, setActiveTab] = useState<string>("trending");
|
||||
const [activeBreedId, setActiveBreedId] = useState<string | null>(null);
|
||||
const [selectedPost, setSelectedPost] = useState<PostCardPost | null>(null);
|
||||
const speciesTabsScroll = useHorizontalScroll<HTMLDivElement>();
|
||||
|
||||
const isTrending = activeTab === "trending";
|
||||
const activeSpeciesId =
|
||||
@@ -94,7 +96,11 @@ export default function ExplorePage() {
|
||||
<h1 className="text-xl font-semibold mb-4">{t("title")}</h1>
|
||||
|
||||
<Tabs value={activeTab} onValueChange={handleTabChange} className="mb-4">
|
||||
<div className="overflow-x-auto overflow-y-hidden scrollbar-hide">
|
||||
<div
|
||||
ref={speciesTabsScroll.ref}
|
||||
onWheel={speciesTabsScroll.onWheel}
|
||||
className="overflow-x-auto overflow-y-hidden scrollbar-hide"
|
||||
>
|
||||
<TabsList className="w-max">
|
||||
<TabsTrigger value="trending" className="gap-1">
|
||||
<TrendingUp className="h-3.5 w-3.5" />
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useTranslations } from "next-intl";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useTRPC } from "@/trpc/client";
|
||||
import { useActivePet } from "@/context/ActivePetContext";
|
||||
import { useHorizontalScroll } from "@/hooks/use-horizontal-scroll";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import {
|
||||
Dialog,
|
||||
@@ -34,6 +35,7 @@ export function StoryTray() {
|
||||
const trpc = useTRPC();
|
||||
const { activePetId } = useActivePet();
|
||||
|
||||
const trayScroll = useHorizontalScroll<HTMLDivElement>();
|
||||
const [addStoryOpen, setAddStoryOpen] = useState(false);
|
||||
const [viewingPet, setViewingPet] = useState<{
|
||||
id: string;
|
||||
@@ -53,6 +55,8 @@ export function StoryTray() {
|
||||
<>
|
||||
{/* Horizontal scroll tray */}
|
||||
<div
|
||||
ref={trayScroll.ref}
|
||||
onWheel={trayScroll.onWheel}
|
||||
className="flex gap-4 overflow-x-auto px-6 py-3 scrollbar-hide"
|
||||
role="list"
|
||||
aria-label={t("storiesAriaLabel")}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { useCallback, useRef, type WheelEvent } from "react";
|
||||
|
||||
/**
|
||||
* Translates vertical mouse-wheel scrolling into horizontal scrolling for a
|
||||
* scrollbar-hide-d container. Touch/trackpad swipes already scroll these
|
||||
* containers natively — this only covers the desktop mouse-wheel case,
|
||||
* which has no other way to move a horizontally-scrolling row with no
|
||||
* visible scrollbar (see Gitea #27).
|
||||
*
|
||||
* Trackpad horizontal gestures (deltaX already present) pass through
|
||||
* untouched, and the handler no-ops when the container isn't actually
|
||||
* overflowing, so it never hijacks normal page scroll.
|
||||
*/
|
||||
export function useHorizontalScroll<T extends HTMLElement>() {
|
||||
const ref = useRef<T>(null);
|
||||
|
||||
const onWheel = useCallback((e: WheelEvent<T>) => {
|
||||
const el = ref.current;
|
||||
if (!el || el.scrollWidth <= el.clientWidth) return;
|
||||
if (Math.abs(e.deltaY) <= Math.abs(e.deltaX)) return;
|
||||
|
||||
el.scrollLeft += e.deltaY;
|
||||
e.preventDefault();
|
||||
}, []);
|
||||
|
||||
return { ref, onWheel };
|
||||
}
|
||||
Reference in New Issue
Block a user