add loading progress support and new indicator

This commit is contained in:
Brian Beck 2025-12-04 14:24:51 -08:00
parent 8e6ae456f0
commit 2a730b8a44
55 changed files with 207 additions and 71609 deletions

View file

@ -26,7 +26,19 @@ function MapInspector() {
const [missionName, setMissionName] = useState(
searchParams.get("mission") || "TWL2_WoodyMyrk",
);
const [isLoading, setIsLoading] = useState(true);
const [loadingProgress, setLoadingProgress] = useState(0);
const [showLoadingIndicator, setShowLoadingIndicator] = useState(true);
const isLoading = loadingProgress < 1;
// Keep the loading indicator visible briefly after reaching 100%
useEffect(() => {
if (isLoading) {
setShowLoadingIndicator(true);
} else {
const timer = setTimeout(() => setShowLoadingIndicator(false), 500);
return () => clearTimeout(timer);
}
}, [isLoading]);
useEffect(() => {
// For automation, like the t2-maps app!
@ -48,17 +60,31 @@ function MapInspector() {
router.replace(`?${params.toString()}`, { scroll: false });
}, [missionName, router]);
const handleLoadingChange = useCallback((loading: boolean) => {
setIsLoading(loading);
}, []);
const handleLoadingChange = useCallback(
(_loading: boolean, progress: number = 0) => {
setLoadingProgress(progress);
},
[],
);
return (
<QueryClientProvider client={queryClient}>
<main>
<SettingsProvider>
<div id="canvasContainer">
{isLoading && (
<div id="loadingIndicator" className="LoadingSpinner" />
{showLoadingIndicator && (
<div id="loadingIndicator" data-complete={!isLoading}>
<div className="LoadingSpinner" />
<div className="LoadingProgress">
<div
className="LoadingProgress-bar"
style={{ width: `${loadingProgress * 100}%` }}
/>
</div>
<div className="LoadingProgress-text">
{Math.round(loadingProgress * 100)}%
</div>
</div>
)}
<Canvas shadows frameloop="always">
<CamerasProvider>

View file

@ -259,22 +259,64 @@ main {
}
.LoadingSpinner {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 48px;
height: 48px;
border: 4px solid rgba(255, 255, 255, 0.2);
border-top-color: white;
border-radius: 50%;
animation: LoadingSpinner-spin 1s linear infinite;
pointer-events: none;
z-index: 1;
}
@keyframes LoadingSpinner-spin {
to {
transform: translate(-50%, -50%) rotate(360deg);
transform: rotate(360deg);
}
}
#loadingIndicator {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
pointer-events: none;
z-index: 1;
opacity: 0.8;
}
#loadingIndicator[data-complete="true"] {
animation: loadingComplete 0.3s ease-out forwards;
}
@keyframes loadingComplete {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.LoadingProgress {
width: 200px;
height: 4px;
background: rgba(255, 255, 255, 0.2);
border-radius: 2px;
overflow: hidden;
}
.LoadingProgress-bar {
height: 100%;
background: white;
border-radius: 2px;
transition: width 0.1s ease-out;
}
.LoadingProgress-text {
font-size: 14px;
color: rgba(255, 255, 255, 0.7);
font-variant-numeric: tabular-nums;
}