feat: add LoadingBar component with loading animation

This commit is contained in:
Jose Selesan
2026-05-28 16:30:34 -03:00
parent 2fa2138bd9
commit d6808fb595
3 changed files with 61 additions and 0 deletions

View File

@@ -2,12 +2,14 @@ import { useState } from "react";
import { Outlet } from "@tanstack/react-router";
import { Header } from "./Header";
import { Sidebar } from "./Sidebar";
import { LoadingBar } from "@/components/ui/loading-bar";
export function Layout() {
const [sidebarOpen, setSidebarOpen] = useState(false);
return (
<div className="flex min-h-screen">
<LoadingBar />
<Sidebar open={sidebarOpen} onClose={() => setSidebarOpen(false)} />
<div className="flex flex-1 flex-col">
<Header onMenuClick={() => setSidebarOpen(true)} />

View File

@@ -0,0 +1,37 @@
import { useState, useEffect, useRef } from "react";
import { useRouterState } from "@tanstack/react-router";
import { useIsFetching, useIsMutating } from "@tanstack/react-query";
import { cn } from "@/lib/utils";
export function LoadingBar() {
const isNavigating = useRouterState({ select: (s) => s.status === "pending" });
const isFetching = useIsFetching();
const isMutating = useIsMutating();
const [visible, setVisible] = useState(false);
const timerRef = useRef<ReturnType<typeof setTimeout>>(undefined);
const loading = isNavigating || isFetching > 0 || isMutating > 0;
useEffect(() => {
if (loading) {
timerRef.current = setTimeout(() => setVisible(true), 200);
} else {
clearTimeout(timerRef.current);
setVisible(false);
}
return () => clearTimeout(timerRef.current);
}, [loading]);
return (
<div
className={cn(
"fixed top-0 left-0 right-0 z-[100] h-0.5 transition-opacity duration-300",
visible ? "opacity-100" : "opacity-0",
)}
aria-hidden="true"
>
<div className="loading-bar h-full w-full bg-primary" />
</div>
);
}

View File

@@ -114,6 +114,28 @@
--sidebar-ring: oklch(0.556 0 0);
}
@keyframes loading-bar {
0% {
transform: scaleX(0);
transform-origin: left;
}
50% {
transform: scaleX(1);
transform-origin: left;
}
51% {
transform-origin: right;
}
100% {
transform: scaleX(0);
transform-origin: right;
}
}
.loading-bar {
animation: loading-bar 1.5s ease-in-out infinite;
}
* {
border-color: var(--border);
}