Spaces:
Sleeping
Sleeping
Download src/components/loading-component.tsx from Chunte/lerobot-visualizer-test: direct link, hf CLI and curl.
- Browser
- Download file 3.47 kB
-
https://huggingface.co/spaces/Chunte/lerobot-visualizer-test/resolve/main/src/components/loading-component.tsx
- Command line
-
hf download hf://spaces/Chunte/lerobot-visualizer-test/src/components/loading-component.tsx
-
curl -L -o loading-component.tsx https://huggingface.co/spaces/Chunte/lerobot-visualizer-test/resolve/main/src/components/loading-component.tsx
3.47 kB
| "use client"; | |
| import { useEffect, useState } from "react"; | |
| import { cx, Spinner } from "@/components/ui"; | |
| import { exitMs, usePresence } from "@/utils/motion"; | |
| /** | |
| * Loading veil over the content area (`absolute`: the nearest positioned ancestor is the episode | |
| * page's content column, which keeps the same geometry from the data-loading shell to the loaded | |
| * page, so the spinner holds its spot through both phases while the header and sidebar stay put). | |
| * | |
| * - `visible={false}` fades the veil out (with clicks passing through) and then unmounts it, so | |
| * the content appears through it instead of popping. | |
| * - `appear={false}` is for taking over from the previous phase's veil: it shows at once, without | |
| * replaying the fade-in (which would read as a blink) or announcing the status a second time. | |
| * A later reopen of the same instance fades in again. Taking over with nothing left to load | |
| * (`visible={false}` from the start, e.g. a view with no loading phase of its own) plays the | |
| * previous veil's fade-out from here instead of letting it vanish. | |
| */ | |
| export default function Loading({ | |
| visible = true, | |
| appear = true, | |
| }: { | |
| visible?: boolean; | |
| appear?: boolean; | |
| } = {}) { | |
| const presence = usePresence(visible, "--duration-fast", 250); | |
| const [handoff, setHandoff] = useState(!appear && !visible); | |
| useEffect(() => { | |
| if (!handoff) return; | |
| const t = window.setTimeout( | |
| () => setHandoff(false), | |
| exitMs("--duration-fast", 250), | |
| ); | |
| return () => window.clearTimeout(t); | |
| }, [handoff]); | |
| const state = handoff && presence === "closed" ? "closing" : presence; | |
| const [fresh, setFresh] = useState(appear); | |
| if (state === "closed" && !fresh) setFresh(true); | |
| if (state === "closed") return null; | |
| const closing = state === "closing"; | |
| return ( | |
| <div | |
| data-state={state} | |
| inert={closing} | |
| aria-hidden={closing || undefined} | |
| role={fresh ? "status" : undefined} | |
| aria-live={fresh ? "polite" : undefined} | |
| className={cx( | |
| "absolute inset-0 z-10 flex flex-col items-center justify-center bg-bg/70 backdrop-blur-md", | |
| // Opacity only: never animate the blur radius of a layer this size. `starting:` only | |
| // applies to a veil mounted already closing (the hand-off above): it fades from opaque. | |
| "transition-opacity duration-(--duration-fast) ease-(--ease-out)", | |
| closing | |
| ? "pointer-events-none opacity-0 starting:opacity-100" | |
| : "opacity-100", | |
| )} | |
| > | |
| <div | |
| className={cx( | |
| "flex flex-col items-center gap-3", | |
| fresh && "animate-fade-in", | |
| )} | |
| > | |
| {/* The rotation lives on this div, not on the SVG: a transformed HTML box runs on the | |
| compositor, so it keeps spinning while the main thread is busy decoding parquet and | |
| building charts. Stepped like the shared Spinner. */} | |
| <div className="h-7 w-7 animate-spin text-fg-secondary [animation-duration:900ms] [animation-timing-function:steps(12)] [will-change:transform]"> | |
| <Spinner className="h-7 w-7 animate-none!" /> | |
| </div> | |
| <div className="text-center"> | |
| <h1 className="text-13 font-medium text-fg-secondary">Loading</h1> | |
| <p className="mt-0.5 text-2xs text-fg-secondary"> | |
| Preparing data and videos | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |