import { type ReactNode } from "react"; import { Loader2 } from "lucide-react"; import { usePullToRefresh } from "../hooks/usePullToRefresh"; const THRESHOLD = 64; interface PullToRefreshProps { children: ReactNode; onRefresh: () => Promise | void; enabled?: boolean; } /** * Touch gesture wrapper that triggers a refresh after pulling down 64px. * Shows a visual indicator with "Pull to refresh" / "Release to refresh" text. */ export function PullToRefresh({ children, onRefresh, enabled = true }: PullToRefreshProps) { const { containerRef, pullDistance, isRefreshing, setIsRefreshing } = usePullToRefresh({ onRefresh: () => { const result = onRefresh(); if (result instanceof Promise) { result.finally(() => setIsRefreshing(false)); } else { setIsRefreshing(false); } }, threshold: THRESHOLD, maxPull: 96, enabled, }); const isVisible = pullDistance > 0 || isRefreshing; const indicatorOpacity = isRefreshing ? 1 : pullDistance / THRESHOLD; const indicatorTranslateY = isRefreshing ? 0 : Math.min(pullDistance - THRESHOLD / 2, 0); return (
{/* Pull indicator */} {isVisible && (
= THRESHOLD ? "Release to refresh" : "Pull to refresh" } > {!isRefreshing && ( {pullDistance >= THRESHOLD ? "Release to refresh" : "Pull to refresh"} )}
)} {/* Content — pushed down by pull distance when pulling */}
0 ? `translateY(${Math.min(pullDistance, 96)}px)` : undefined, transition: pullDistance === 0 ? "transform 200ms ease-out" : undefined, }} > {children}
); }