Skip to content

Commit 64f7bfe

Browse files
authored
feat: mo.ui.file_browser show spinner when listing is slow (#7155)
## 📝 Summary Adds a spinner to `mo.ui.file_browser` if listing files takes more than 200ms. ## 🔍 Description of Changes I was using `mo.ui.file_browser` with S3, browsing a medium size directory and the initial load takes something like 20 seconds without any indication for what's happening. Felt a bit broken so this changes that. The delay is there to avoid flashing the spinner when listing is quick. Here's a demo with the change: https://github.com/user-attachments/assets/b68722e9-d157-4342-befc-090f5b367d4a ## 📋 Checklist - [x] I have read the [contributor guidelines](https://github.com/marimo-team/marimo/blob/main/CONTRIBUTING.md). - [ ] For large changes, or changes that affect the public API: this change was discussed or approved through an issue, on [Discord](https://marimo.io/discord?ref=pr), or the community [discussions](https://github.com/marimo-team/marimo/discussions) (Please provide a link if applicable). - [ ] I have added tests for the changes made. - [x] I have run the code and verified that it works as expected. I have read the CLA Document and I hereby sign the CLA.
1 parent 46f6719 commit 64f7bfe

File tree

1 file changed

+30
-5
lines changed

1 file changed

+30
-5
lines changed

frontend/src/plugins/impl/FileBrowserPlugin.tsx

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
/* Copyright 2024 Marimo. All rights reserved. */
22

33
import { CornerLeftUp } from "lucide-react";
4-
import { type JSX, useState } from "react";
4+
import { type JSX, useEffect, useState } from "react";
55
import { z } from "zod";
66
import {
77
FILE_TYPE_ICONS,
88
type FileType,
99
guessFileType,
1010
} from "@/components/editor/file-tree/types";
11+
import { Spinner } from "@/components/icons/spinner";
1112
import { Button } from "@/components/ui/button";
1213
import { Checkbox } from "@/components/ui/checkbox";
1314
import { Label } from "@/components/ui/label";
@@ -145,6 +146,7 @@ export const FileBrowser = ({
145146
const [path, setPath] = useInternalStateWithSync(initialPath);
146147
const [selectAllLabel, setSelectAllLabel] = useState("Select all");
147148
const [isUpdatingPath, setIsUpdatingPath] = useState(false);
149+
const [showLoadingOverlay, setShowLoadingOverlay] = useState(false);
148150

149151
// HACK: use the random-id of the host element to force a re-render
150152
// when the random-id changes, this means the cell was re-rendered
@@ -153,10 +155,22 @@ export const FileBrowser = ({
153155
const { data, error, isPending } = useAsyncData(() => {
154156
return list_directory({ path: path });
155157
}, [path, randomId]);
158+
const spinnerLabel = "Listing files...";
156159

157-
if (isPending) {
158-
return null;
159-
}
160+
useEffect(() => {
161+
if (!isPending) {
162+
setShowLoadingOverlay(false);
163+
return;
164+
}
165+
166+
const timeout = window.setTimeout(() => {
167+
setShowLoadingOverlay(true);
168+
}, 200);
169+
170+
return () => {
171+
window.clearTimeout(timeout);
172+
};
173+
}, [isPending]);
160174

161175
if (!data && error) {
162176
return <Banner kind="danger">{error.message}</Banner>;
@@ -459,9 +473,20 @@ export const FileBrowser = ({
459473
)}
460474

461475
<div
462-
className="mt-3 overflow-y-auto w-full border"
476+
className="mt-3 overflow-y-auto w-full border relative"
463477
style={{ height: "14rem" }}
478+
aria-busy={isPending}
479+
aria-live="polite"
464480
>
481+
{showLoadingOverlay && (
482+
<div
483+
className="absolute inset-0 flex flex-col items-center justify-center gap-2 bg-background/80 text-xs text-muted-foreground pointer-events-none z-10"
484+
role="status"
485+
>
486+
<Spinner size="small" />
487+
<span>{spinnerLabel}</span>
488+
</div>
489+
)}
465490
<Table className="cursor-pointer table-fixed">
466491
<TableBody>{fileRows}</TableBody>
467492
</Table>

0 commit comments

Comments
 (0)