FileTree can reveal a path
For a tree whose selection is driven from outside — a nav beside it, a URL — where a folder left shut around the open file hides the one thing being looked at. Every directory on the way opens, loading as it goes. It only ever opens. A node that closed itself when the selection moved elsewhere would collapse the tree under somebody as they clicked around it.
This commit is contained in:
@@ -21,6 +21,14 @@ type FileTreeProps = {
|
||||
* an agent had just written to showing the listing from before its turn.
|
||||
*/
|
||||
refreshToken?: number;
|
||||
/**
|
||||
* A file to reveal: every directory on the way to it opens, loading as it goes.
|
||||
*
|
||||
* For a tree whose selection is driven from OUTSIDE — a nav beside it, a URL — where a folder left
|
||||
* shut around the open file hides the one thing being looked at. Expanding is still disclosure and
|
||||
* still remembered, so anything opened by hand stays open.
|
||||
*/
|
||||
revealPath?: string | null;
|
||||
};
|
||||
|
||||
type TreeNodeProps = {
|
||||
@@ -31,6 +39,7 @@ type TreeNodeProps = {
|
||||
searchForFile?: ((path: string) => string) | null;
|
||||
depth: number;
|
||||
refreshToken?: number;
|
||||
revealPath?: string | null;
|
||||
};
|
||||
|
||||
const FileIcon = ({ name }: { name: string }) => {
|
||||
@@ -44,7 +53,16 @@ const sortEntries = (entries: DirEntry[]) => {
|
||||
return [...dirs, ...files];
|
||||
};
|
||||
|
||||
const TreeNode = ({ entry, parentPath, root, onOpenFile, searchForFile, depth, refreshToken }: TreeNodeProps) => {
|
||||
const TreeNode = ({
|
||||
entry,
|
||||
parentPath,
|
||||
root,
|
||||
onOpenFile,
|
||||
searchForFile,
|
||||
depth,
|
||||
refreshToken,
|
||||
revealPath,
|
||||
}: TreeNodeProps) => {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const [children, setChildren] = useState<DirEntry[] | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -73,6 +91,32 @@ const TreeNode = ({ entry, parentPath, root, onOpenFile, searchForFile, depth, r
|
||||
};
|
||||
}, [refreshToken]);
|
||||
|
||||
/*
|
||||
On the way to the revealed file: open, and load if this is the first time.
|
||||
|
||||
Only ever opens. A node that closes itself when the selection moves elsewhere would collapse the
|
||||
tree under somebody as they clicked around it.
|
||||
*/
|
||||
const onTheWay = !!revealPath && isDir && revealPath.startsWith(`${fullPath}/`);
|
||||
|
||||
useEffect(() => {
|
||||
if (!onTheWay) return;
|
||||
setExpanded(true);
|
||||
if (children !== null) return;
|
||||
|
||||
let alive = true;
|
||||
listDirRef
|
||||
.current(fullPath)
|
||||
.then((res) => alive && setChildren(sortEntries(res.entries)))
|
||||
.catch(() => alive && setChildren([]));
|
||||
return () => {
|
||||
alive = false;
|
||||
};
|
||||
// `children` is deliberately absent: this must run when the PATH changes, not when the load it
|
||||
// started completes.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [onTheWay, fullPath]);
|
||||
|
||||
const handleClick = async () => {
|
||||
if (!isDir) {
|
||||
onOpenFile(fullPath, entry.name);
|
||||
@@ -140,6 +184,7 @@ const TreeNode = ({ entry, parentPath, root, onOpenFile, searchForFile, depth, r
|
||||
searchForFile={searchForFile}
|
||||
depth={depth + 1}
|
||||
refreshToken={refreshToken}
|
||||
revealPath={revealPath}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -148,7 +193,7 @@ const TreeNode = ({ entry, parentPath, root, onOpenFile, searchForFile, depth, r
|
||||
);
|
||||
};
|
||||
|
||||
export const FileTree = ({ root, basePath, onOpenFile, searchForFile, refreshToken }: FileTreeProps) => {
|
||||
export const FileTree = ({ root, basePath, onOpenFile, searchForFile, refreshToken, revealPath }: FileTreeProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { listDir } = useFilesAPI(root);
|
||||
const [entries, setEntries] = useState<DirEntry[] | null>(null);
|
||||
@@ -208,6 +253,7 @@ export const FileTree = ({ root, basePath, onOpenFile, searchForFile, refreshTok
|
||||
searchForFile={searchForFile}
|
||||
depth={0}
|
||||
refreshToken={refreshToken}
|
||||
revealPath={revealPath}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user