diff --git a/src/scene.tsx b/src/scene.tsx index bc368e06..6cfede9c 100644 --- a/src/scene.tsx +++ b/src/scene.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef, useState } from "react"; -import { Button, InputSlider, InputText, InputToggle } from "@czi-sds/components"; +import { Button, InputSlider, InputText, InputToggle, LoadingIndicator } from "@czi-sds/components"; import { PointCanvas } from "./PointCanvas"; import { TrackManager, loadTrackManager } from "./TrackManager"; @@ -26,6 +26,7 @@ export default function Scene(props: SceneProps) { const [curTime, setCurTime] = useState(0); const [autoRotate, setAutoRotate] = useState(false); const [playing, setPlaying] = useState(false); + const [loading, setLoading] = useState(false); // Use references here for two things: // * manage objects that should never change, even when the component re-renders @@ -113,6 +114,8 @@ export default function Scene(props: SceneProps) { // update the points when the array or timepoint changes useEffect(() => { setSelectedPoints({}); + // show a loading indicator if the fetch takes longer than 10ms (avoid flicker) + const loadingTimer = setTimeout(() => setLoading(true), 10); let ignore = false; // TODO: this is a very basic attempt to prevent stale data // in addition, we should debounce the input and verify the data is current @@ -127,12 +130,17 @@ export default function Scene(props: SceneProps) { console.debug("IGNORE SET points at time %d", curTime); return; } + clearTimeout(loadingTimer); + setLoading(false); canvas.current?.setPointsPositions(data); }); } else { + clearTimeout(loadingTimer); + setLoading(false); console.debug("IGNORE FETCH points at time %d", curTime); } return () => { + clearTimeout(loadingTimer); ignore = true; }; }, [trackManager, curTime]); @@ -150,55 +158,58 @@ export default function Scene(props: SceneProps) { marks.push({ value: numTimes - 1, label: numTimes - 1 }); return ( -
-
- setDataUrl(new URL(e.target.value))} - fullWidth={true} - intent={trackManager ? "default" : "error"} - /> - setCurTime(value as number)} - marks={marks} - value={curTime} - /> -
- { - setAutoRotate((e.target as HTMLInputElement).checked); - }} +
+
+
+ setDataUrl(new URL(e.target.value))} + fullWidth={true} + intent={trackManager ? "default" : "error"} /> - { - setPlaying((e.target as HTMLInputElement).checked); - }} + min={0} + max={numTimes - 1} + valueLabelDisplay="on" + onChange={(_, value) => setCurTime(value as number)} + marks={marks} + value={curTime} /> - +
+ { + setAutoRotate((e.target as HTMLInputElement).checked); + }} + /> + { + setPlaying((e.target as HTMLInputElement).checked); + }} + /> + +
+ {loading && }
); }