tahamajs/IE / Projects /front /src /context /LoadingContext.tsx
tahamajs's picture
download
raw
4.31 kB
// src/context/LoadingContext.tsx
import { createContext, useContext, useState,useEffect, useCallback, useRef, ReactNode } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Loader2 } from "lucide-react";
// ============================================
// Types
// ============================================
interface LoadingItem {
id: string;
message?: string;
}
interface LoadingContextValue {
isLoading: boolean;
loadingCount: number;
loadingMessages: string[];
startLoading: (id?: string, message?: string) => string;
stopLoading: (id: string) => void;
stopAllLoading: () => void;
withLoading: <T>(promise: Promise<T>, message?: string) => Promise<T>;
}
// ============================================
// Context
// ============================================
const LoadingContext = createContext<LoadingContextValue | null>(null);
export function LoadingProvider({ children }: { children: ReactNode }) {
const [loadingItems, setLoadingItems] = useState<LoadingItem[]>([]);
const loadingRef = useRef(loadingItems);
// Keep ref in sync
useEffect(() => {
loadingRef.current = loadingItems;
}, [loadingItems]);
// ============================================
// Start Loading
// ============================================
const startLoading = useCallback((id?: string, message?: string): string => {
const loadingId = id || `${Date.now()}-${Math.random().toString(36).substr(2, 6)}`;
setLoadingItems(prev => [...prev, { id: loadingId, message }]);
return loadingId;
}, []);
// ============================================
// Stop Loading
// ============================================
const stopLoading = useCallback((id: string) => {
setLoadingItems(prev => prev.filter(item => item.id !== id));
}, []);
// ============================================
// Stop All Loading
// ============================================
const stopAllLoading = useCallback(() => {
setLoadingItems([]);
}, []);
// ============================================
// With Loading
// ============================================
const withLoading = useCallback(async <T,>(
promise: Promise<T>,
message?: string
): Promise<T> => {
const id = startLoading(undefined, message);
try {
const result = await promise;
return result;
} finally {
stopLoading(id);
}
}, [startLoading, stopLoading]);
// ============================================
// Derived State
// ============================================
const isLoading = loadingItems.length > 0;
const loadingMessages = loadingItems.map(item => item.message).filter(Boolean) as string[];
// ============================================
// Value
// ============================================
const value: LoadingContextValue = {
isLoading,
loadingCount: loadingItems.length,
loadingMessages,
startLoading,
stopLoading,
stopAllLoading,
withLoading,
};
return (
<LoadingContext.Provider value={value}>
{children}
<AnimatePresence>
{isLoading && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 z-[200] flex items-center justify-center bg-black/20 backdrop-blur-sm pointer-events-none"
>
<div className="bg-white dark:bg-gray-800 rounded-2xl p-6 shadow-xl flex flex-col items-center gap-3 pointer-events-auto">
<motion.div
animate={{ rotate: 360 }}
transition={{ duration: 1, repeat: Infinity, ease: "linear" }}
>
<Loader2 className="w-8 h-8 text-primary" />
</motion.div>
{loadingMessages.length > 0 && (
<p className="text-sm text-gray-600 dark:text-gray-300">
{loadingMessages[loadingMessages.length - 1]}
</p>
)}
</div>
</motion.div>
)}
</AnimatePresence>
</LoadingContext.Provider>
);
}
export const useLoading = () => {
const ctx = useContext(LoadingContext);
if (!ctx) throw new Error("useLoading must be used within LoadingProvider");
return ctx;
};

Xet Storage Details

Size:
4.31 kB
·
Xet hash:
37012e1df1aea2eddc1a73b7802960330d1d0bd0cb0405847958642f85dbf679

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.