fix: filetree wrong opening order

This commit is contained in:
Nicolas Meienberger 2025-10-05 21:13:22 +02:00
parent ac3e2a6b01
commit eb5fae3ab7

View file

@ -32,8 +32,8 @@ interface Props {
className?: string; className?: string;
} }
export const FileTree = memo( export const FileTree = memo((props: Props) => {
({ const {
files = [], files = [],
onFileSelect, onFileSelect,
selectedFile, selectedFile,
@ -41,118 +41,107 @@ export const FileTree = memo(
expandedFolders = new Set(), expandedFolders = new Set(),
loadingFolders = new Set(), loadingFolders = new Set(),
className, className,
}: Props) => { } = props;
const fileList = useMemo(() => {
return buildFileList(files);
}, [files]);
const [collapsedFolders, setCollapsedFolders] = useState<Set<string>>(new Set()); const fileList = useMemo(() => {
return buildFileList(files);
}, [files]);
const filteredFileList = useMemo(() => { const [collapsedFolders, setCollapsedFolders] = useState<Set<string>>(new Set());
const list = [];
let lastDepth = Number.MAX_SAFE_INTEGER;
for (const fileOrFolder of fileList) { const filteredFileList = useMemo(() => {
const depth = fileOrFolder.depth; const list = [];
let lastDepth = Number.MAX_SAFE_INTEGER;
// if the depth is equal we reached the end of the collapsed group for (const fileOrFolder of fileList) {
if (lastDepth === depth) { const depth = fileOrFolder.depth;
lastDepth = Number.MAX_SAFE_INTEGER;
}
// ignore collapsed folders // if the depth is equal we reached the end of the collapsed group
if (collapsedFolders.has(fileOrFolder.fullPath)) { if (lastDepth === depth) {
lastDepth = Math.min(lastDepth, depth); lastDepth = Number.MAX_SAFE_INTEGER;
}
// ignore files and folders below the last collapsed folder
if (lastDepth < depth) {
continue;
}
list.push(fileOrFolder);
} }
return list; // ignore collapsed folders
}, [fileList, collapsedFolders]); if (collapsedFolders.has(fileOrFolder.fullPath)) {
lastDepth = Math.min(lastDepth, depth);
}
const toggleCollapseState = (fullPath: string) => { // ignore files and folders below the last collapsed folder
setCollapsedFolders((prevSet) => { if (lastDepth < depth) {
const newSet = new Set(prevSet); continue;
}
if (newSet.has(fullPath)) { list.push(fileOrFolder);
newSet.delete(fullPath); }
onFolderExpand?.(fullPath);
} else { return list;
newSet.add(fullPath); }, [fileList, collapsedFolders]);
const toggleCollapseState = (fullPath: string) => {
setCollapsedFolders((prevSet) => {
const newSet = new Set(prevSet);
if (newSet.has(fullPath)) {
newSet.delete(fullPath);
onFolderExpand?.(fullPath);
} else {
newSet.add(fullPath);
}
return newSet;
});
};
// Add new folders to collapsed set when file list changes
useEffect(() => {
setCollapsedFolders((prevSet) => {
const newSet = new Set(prevSet);
for (const item of fileList) {
if (item.kind === "folder" && !newSet.has(item.fullPath) && !expandedFolders.has(item.fullPath)) {
newSet.add(item.fullPath);
} }
}
return newSet;
});
}, [fileList, expandedFolders]);
return newSet; return (
}); <div className={cn("text-sm", className)}>
}; {filteredFileList.map((fileOrFolder) => {
switch (fileOrFolder.kind) {
// Add new folders to collapsed set when file list changes case "file": {
useEffect(() => { return (
setCollapsedFolders((prevSet) => { <File
const newSet = new Set(prevSet); key={fileOrFolder.id}
for (const item of fileList) { selected={selectedFile === fileOrFolder.fullPath}
if (item.kind === "folder" && !newSet.has(item.fullPath) && !expandedFolders.has(item.fullPath)) { file={fileOrFolder}
newSet.add(item.fullPath); onClick={() => {
onFileSelect?.(fileOrFolder.fullPath);
}}
/>
);
}
case "folder": {
return (
<Folder
key={fileOrFolder.id}
folder={fileOrFolder}
collapsed={collapsedFolders.has(fileOrFolder.fullPath)}
loading={loadingFolders.has(fileOrFolder.fullPath)}
onClick={() => {
toggleCollapseState(fileOrFolder.fullPath);
}}
/>
);
}
default: {
return undefined;
} }
} }
return newSet; })}
}); </div>
}, [fileList, expandedFolders]); );
});
// Expand folders that are in the expandedFolders set
useEffect(() => {
setCollapsedFolders((prevSet) => {
const newSet = new Set(prevSet);
for (const folder of expandedFolders) {
newSet.delete(folder);
}
return newSet;
});
}, [expandedFolders]);
return (
<div className={cn("text-sm", className)}>
{filteredFileList.map((fileOrFolder) => {
switch (fileOrFolder.kind) {
case "file": {
return (
<File
key={fileOrFolder.id}
selected={selectedFile === fileOrFolder.fullPath}
file={fileOrFolder}
onClick={() => {
onFileSelect?.(fileOrFolder.fullPath);
}}
/>
);
}
case "folder": {
return (
<Folder
key={fileOrFolder.id}
folder={fileOrFolder}
collapsed={collapsedFolders.has(fileOrFolder.fullPath)}
loading={loadingFolders.has(fileOrFolder.fullPath)}
onClick={() => {
toggleCollapseState(fileOrFolder.fullPath);
}}
/>
);
}
default: {
return undefined;
}
}
})}
</div>
);
},
);
interface FolderProps { interface FolderProps {
folder: FolderNode; folder: FolderNode;