next-navigation-progress
Version:
A customizable navigation progress bar for Next.js apps with React 19 support
1 lines • 10.3 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","sources":["../src/index.tsx"],"sourcesContent":["import { useRouter } from 'next/navigation';\nimport Link from 'next/link';\nimport {\n createContext,\n memo,\n startTransition,\n useContext,\n useEffect,\n useOptimistic,\n useState,\n} from 'react';\n\nfunction easeOutQuad(t: number) {\n return t * (2 - t);\n}\n\nfunction generateEaseOutArray(length: number) {\n const result = [];\n for (let i = 1; i <= length; i++) {\n const easedValue = Math.round(easeOutQuad(i / (length - 1)) * 100);\n result.push(easedValue);\n }\n return result;\n}\n\nconst Context = createContext<{\n progress: number;\n startNewProgress: () => void;\n optimisticObj: { loading: boolean };\n stateObj: { showing: boolean };\n}>({\n progress: 0,\n startNewProgress: () => {},\n optimisticObj: { loading: false },\n stateObj: { showing: false },\n});\n\n/**\n * Internal hook for managing progress state\n * @internal\n */\nexport const useProgress = () => {\n const [optimisticObj, setOptimisticObj] = useOptimistic({\n loading: false,\n });\n const [stateObj, setStateObj] = useState({\n showing: false,\n }); // to show latest status, use obj instead of just boolean\n const [progress, setProgress] = useState(5);\n\n useEffect(() => {\n if (!optimisticObj.loading) {\n return;\n }\n\n setStateObj(() => {\n return {\n showing: true,\n };\n });\n setProgress(0);\n }, [optimisticObj]);\n\n useEffect(() => {\n if (optimisticObj.loading || !stateObj.showing) {\n return;\n }\n\n const timeout = setTimeout(() => {\n setProgress(0);\n setStateObj((obj) => {\n return {\n ...obj,\n showing: false,\n };\n });\n }, 300);\n\n return () => {\n clearTimeout(timeout);\n };\n }, [optimisticObj.loading, stateObj.showing]);\n\n useEffect(() => {\n if (!stateObj.showing) {\n return;\n }\n\n let index = 0;\n const arr = generateEaseOutArray(100).map((val) => val * (90 / 100));\n\n const updateProgress = () => {\n setProgress((prev) => {\n if (!optimisticObj.loading) {\n return 100;\n }\n\n const val = arr[index];\n index++;\n\n if (!val) {\n return prev;\n }\n\n return val;\n });\n };\n\n const interval = setInterval(updateProgress, 100);\n updateProgress();\n\n return () => {\n clearInterval(interval);\n };\n }, [stateObj, optimisticObj.loading]);\n\n const startNewProgress = () => {\n setOptimisticObj({\n loading: true,\n });\n };\n\n return {\n startNewProgress,\n progress,\n optimisticObj,\n stateObj,\n };\n};\n\n/**\n * Provider component that manages navigation progress state\n * Wrap your application with this provider to enable navigation progress functionality\n * \n * @example\n * ```tsx\n * <NextNavigationProgressProvider>\n * <NextNavigationProgressBar />\n * {children}\n * </NextNavigationProgressProvider>\n * ```\n */\nexport const NextNavigationProgressProvider = ({\n children,\n}: {\n children: React.ReactNode;\n}) => {\n const { progress, startNewProgress, optimisticObj, stateObj } = useProgress();\n\n return (\n <Context.Provider\n value={{ progress, startNewProgress, optimisticObj, stateObj }}\n >\n {children}\n </Context.Provider>\n );\n};\n\n/**\n * Progress bar component that displays navigation progress\n * Must be used inside NextNavigationProgressProvider\n * \n * @example\n * ```tsx\n * <NextNavigationProgressProvider>\n * <NextNavigationProgressBar />\n * {children}\n * </NextNavigationProgressProvider>\n * ```\n */\nexport const NextNavigationProgressBar = () => {\n const { progress, stateObj } = useContext(Context);\n\n return (\n <div\n style={{\n display: stateObj.showing ? 'block' : 'none',\n }}\n >\n <div\n style={{\n width: stateObj.showing ? `${progress}%` : 0,\n height: '3px',\n backgroundColor: '#228be6',\n transition: stateObj.showing ? 'width 0.3s' : 'none',\n opacity: stateObj.showing ? 1 : 0,\n }}\n ></div>\n </div>\n );\n};\n\n/**\n * Hook to access navigation progress state and controls\n * Must be used inside NextNavigationProgressProvider\n * \n * @returns {Object} Progress state and control functions\n * @returns {number} progress - Current progress value (0-100)\n * @returns {Function} startNewProgress - Function to start a new progress animation\n * @returns {Object} optimisticObj - Object containing loading state\n * @returns {Object} stateObj - Object containing visibility state\n * \n * @example\n * ```tsx\n * const { progress, startNewProgress, optimisticObj, stateObj } = useNavigationProgress();\n * ```\n */\nexport const useNavigationProgress = () => {\n return useContext(Context);\n};\n\n/**\n * Pre-configured Link component that automatically triggers navigation progress\n * Wraps Next.js Link component with progress bar integration\n * \n * @param {React.ReactNode} children - Link content\n * @param {string} className - CSS class name\n * @param {string} href - Destination URL\n * @param {string} [target] - Link target attribute\n * @param {Function} [onClick] - Custom click handler\n * @param {boolean} [prefetch] - Next.js prefetch behavior\n * @param {React.CSSProperties} [style] - Inline styles\n * \n * @example\n * ```tsx\n * <NavigationLink href=\"/about\" className=\"nav-link\">\n * About Page\n * </NavigationLink>\n * ```\n */\nexport const NavigationLink = memo(function NavigationLink({\n children,\n className,\n href,\n target,\n onClick,\n prefetch,\n style,\n}: {\n children: React.ReactNode;\n className?: string;\n href: string;\n onClick?: React.MouseEventHandler<HTMLAnchorElement>;\n target?: string;\n prefetch?: boolean;\n style?: React.CSSProperties;\n}) {\n const router = useRouter();\n const { startNewProgress } = useNavigationProgress();\n\n return (\n <Link\n prefetch={prefetch}\n className={className}\n href={href}\n onClick={(e) => {\n if (onClick) {\n onClick(e);\n return;\n }\n\n startTransition(async () => {\n startNewProgress();\n router.push(href);\n });\n }}\n target={target}\n style={style}\n >\n {children}\n </Link>\n );\n});\n"],"names":["_jsx"],"mappings":"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"}