From dad1da471219e5e0704fb0b40d2067cb1a63b470 Mon Sep 17 00:00:00 2001 From: Guga Guichard Date: Mon, 5 Feb 2024 09:08:31 -0300 Subject: [PATCH 1/2] feat: Add global loading indicator to top nav --- app/root.tsx | 2 ++ app/ui/global-loading.tsx | 42 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 app/ui/global-loading.tsx 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..a1b8b3eb --- /dev/null +++ b/app/ui/global-loading.tsx @@ -0,0 +1,42 @@ +import * as React from "react"; +import { useNavigation } from "@remix-run/react"; +import cx from "clsx"; + +export function GlobalLoading() { + const transition = useNavigation(); + const active = transition.state !== "idle"; + + const ref = React.useRef(null); + const [animationComplete, setAnimationComplete] = React.useState(true); + + React.useEffect(() => { + if (!ref.current) return; + if (active) setAnimationComplete(false); + + Promise.allSettled( + ref.current.getAnimations().map(({ finished }) => finished), + ).then(() => !active && setAnimationComplete(true)); + }, [active]); + + return ( +
+
+
+ ); +} From eceb9abd7295349b31f6f0559f95e320850b06e7 Mon Sep 17 00:00:00 2001 From: Guga Guichard Date: Mon, 5 Feb 2024 14:41:05 -0300 Subject: [PATCH 2/2] chore: Apply some of Brook's suggestions to global-loading code --- app/ui/global-loading.tsx | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/app/ui/global-loading.tsx b/app/ui/global-loading.tsx index a1b8b3eb..fabec0bd 100644 --- a/app/ui/global-loading.tsx +++ b/app/ui/global-loading.tsx @@ -1,21 +1,27 @@ -import * as React from "react"; +import { useEffect, useRef, useState } from "react"; import { useNavigation } from "@remix-run/react"; import cx from "clsx"; export function GlobalLoading() { - const transition = useNavigation(); - const active = transition.state !== "idle"; + let transition = useNavigation(); + let active = transition.state !== "idle"; - const ref = React.useRef(null); - const [animationComplete, setAnimationComplete] = React.useState(true); + let ref = useRef(null); + let [animating, setAnimating] = useState(false); - React.useEffect(() => { + useEffect(() => { if (!ref.current) return; - if (active) setAnimationComplete(false); Promise.allSettled( ref.current.getAnimations().map(({ finished }) => finished), - ).then(() => !active && setAnimationComplete(true)); + ).then(() => { + if (!active) setAnimating(false); + }); + + if (active) { + let id = setTimeout(() => setAnimating(true), 100); + return () => clearTimeout(id); + } }, [active]); return ( @@ -30,11 +36,9 @@ export function GlobalLoading() { className={cx( "h-full bg-gradient-to-r from-blue-brand to-aqua-brand transition-all duration-500 ease-in-out", transition.state === "idle" && - animationComplete && - "w-0 opacity-0 transition-none", + (animating ? "w-full" : "w-0 opacity-0 transition-none"), transition.state === "submitting" && "w-4/12", transition.state === "loading" && "w-10/12", - transition.state === "idle" && !animationComplete && "w-full", )} />