diff --git a/app/root.tsx b/app/root.tsx index 2d2c6457..e889f1e9 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -25,6 +25,7 @@ import { parseColorScheme } from "~/lib/color-scheme.server"; import iconsHref from "~/icons.svg"; import cx from "clsx"; import { canUseDOM } from "./ui/primitives/utils"; +import { GlobalLoading } from "./ui/global-loading"; export async function loader({ request }: LoaderFunctionArgs) { removeTrailingSlashes(request); @@ -128,6 +129,7 @@ function Document({ : "bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-200", )} > + {children} diff --git a/app/ui/global-loading.tsx b/app/ui/global-loading.tsx new file mode 100644 index 00000000..fabec0bd --- /dev/null +++ b/app/ui/global-loading.tsx @@ -0,0 +1,46 @@ +import { useEffect, useRef, useState } from "react"; +import { useNavigation } from "@remix-run/react"; +import cx from "clsx"; + +export function GlobalLoading() { + let transition = useNavigation(); + let active = transition.state !== "idle"; + + let ref = useRef(null); + let [animating, setAnimating] = useState(false); + + useEffect(() => { + if (!ref.current) return; + + Promise.allSettled( + ref.current.getAnimations().map(({ finished }) => finished), + ).then(() => { + if (!active) setAnimating(false); + }); + + if (active) { + let id = setTimeout(() => setAnimating(true), 100); + return () => clearTimeout(id); + } + }, [active]); + + return ( +
+
+
+ ); +}