Some checks failed
Branch Build CE / Build Setup (push) Has been cancelled
Branch Build CE / Build-Push Admin Docker Image (push) Has been cancelled
Branch Build CE / Build-Push Web Docker Image (push) Has been cancelled
Branch Build CE / Build-Push Space Docker Image (push) Has been cancelled
Branch Build CE / Build-Push Live Collaboration Docker Image (push) Has been cancelled
Branch Build CE / Build-Push API Server Docker Image (push) Has been cancelled
Branch Build CE / Build-Push Proxy Docker Image (push) Has been cancelled
Branch Build CE / Build-Push AIO Docker Image (push) Has been cancelled
Branch Build CE / Upload Build Assets (push) Has been cancelled
Branch Build CE / Build Release (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Codespell / Check for spelling errors (push) Has been cancelled
Sync Repositories / sync_changes (push) Has been cancelled
Synced from upstream: 8853637e981ed7d8a6cff32bd98e7afe20f54362
141 lines
3.5 KiB
TypeScript
141 lines
3.5 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef } from "react";
|
|
import { BProgress } from "@bprogress/core";
|
|
import { useNavigation } from "react-router";
|
|
import "@bprogress/core/css";
|
|
|
|
/**
|
|
* Progress bar configuration options
|
|
*/
|
|
interface ProgressConfig {
|
|
/** Whether to show the loading spinner */
|
|
showSpinner: boolean;
|
|
/** Minimum progress percentage (0-1) */
|
|
minimum: number;
|
|
/** Animation speed in milliseconds */
|
|
speed: number;
|
|
/** Auto-increment speed in milliseconds */
|
|
trickleSpeed: number;
|
|
/** CSS easing function */
|
|
easing: string;
|
|
/** Enable auto-increment */
|
|
trickle: boolean;
|
|
/** Delay before showing progress bar in milliseconds */
|
|
delay: number;
|
|
/** Whether to disable the progress bar */
|
|
isDisabled?: boolean;
|
|
}
|
|
|
|
/**
|
|
* Configuration for the progress bar
|
|
*/
|
|
const PROGRESS_CONFIG: Readonly<ProgressConfig> = {
|
|
showSpinner: false,
|
|
minimum: 0.1,
|
|
speed: 400,
|
|
trickleSpeed: 800,
|
|
easing: "ease",
|
|
trickle: true,
|
|
delay: 0,
|
|
isDisabled: import.meta.env.PROD, // Disable progress bar in production builds
|
|
} as const;
|
|
|
|
/**
|
|
* Navigation Progress Bar Component
|
|
*
|
|
* Automatically displays a progress bar at the top of the page during React Router navigation.
|
|
* Integrates with React Router's useNavigation hook to monitor route changes.
|
|
*
|
|
* Note: Progress bar is disabled in production builds.
|
|
*
|
|
* @returns null - This component doesn't render any visible elements
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* function App() {
|
|
* return (
|
|
* <>
|
|
* <AppProgressBar />
|
|
* <Outlet />
|
|
* </>
|
|
* );
|
|
* }
|
|
* ```
|
|
*/
|
|
export function AppProgressBar(): null {
|
|
const navigation = useNavigation();
|
|
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
const startedRef = useRef<boolean>(false);
|
|
|
|
// Initialize BProgress once on mount
|
|
useEffect(() => {
|
|
// Skip initialization in production builds
|
|
if (PROGRESS_CONFIG.isDisabled) {
|
|
return;
|
|
}
|
|
|
|
// Configure BProgress with our settings
|
|
BProgress.configure({
|
|
showSpinner: PROGRESS_CONFIG.showSpinner,
|
|
minimum: PROGRESS_CONFIG.minimum,
|
|
speed: PROGRESS_CONFIG.speed,
|
|
trickleSpeed: PROGRESS_CONFIG.trickleSpeed,
|
|
easing: PROGRESS_CONFIG.easing,
|
|
trickle: PROGRESS_CONFIG.trickle,
|
|
});
|
|
|
|
// Render the progress bar element in the DOM
|
|
BProgress.render(true);
|
|
|
|
// Cleanup on unmount
|
|
return () => {
|
|
if (BProgress.isStarted()) {
|
|
BProgress.done();
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
// Handle navigation state changes
|
|
useEffect(() => {
|
|
// Skip navigation tracking in production builds
|
|
if (PROGRESS_CONFIG.isDisabled) {
|
|
return;
|
|
}
|
|
|
|
if (navigation.state === "idle") {
|
|
// Navigation complete - clear any pending timer
|
|
if (timerRef.current !== null) {
|
|
clearTimeout(timerRef.current);
|
|
timerRef.current = null;
|
|
}
|
|
|
|
// Complete progress if it was started
|
|
if (startedRef.current) {
|
|
BProgress.done();
|
|
startedRef.current = false;
|
|
}
|
|
} else {
|
|
// Navigation in progress (loading or submitting)
|
|
// Only start if not already started and no timer pending
|
|
if (timerRef.current === null && !startedRef.current) {
|
|
timerRef.current = setTimeout((): void => {
|
|
if (!BProgress.isStarted()) {
|
|
BProgress.start();
|
|
startedRef.current = true;
|
|
}
|
|
timerRef.current = null;
|
|
}, PROGRESS_CONFIG.delay);
|
|
}
|
|
}
|
|
|
|
return () => {
|
|
if (timerRef.current !== null) {
|
|
clearTimeout(timerRef.current);
|
|
}
|
|
};
|
|
}, [navigation.state]);
|
|
|
|
return null;
|
|
}
|