fix: FileTree duplication and corruption on expand
- Replace recursive buildTree with buildVisibleNodes approach - Build flat list of visible nodes based on expanded state - Use processNode helper to traverse tree structure correctly - Each node appears exactly once in the rendered output - Eliminates duplication when expanding directories
This commit is contained in:
parent
3062420e99
commit
71f213d8d1
@ -134,40 +134,32 @@ export default function FileTree({ workspaceMode, onFileSelect }: FileTreeProps)
|
||||
}
|
||||
}, [onFileSelect]);
|
||||
|
||||
// Build tree structure from flat entries
|
||||
const buildTree = useCallback((entries: FileTreeEntry[], depth: number = 0) => {
|
||||
const nodes: JSX.Element[] = [];
|
||||
// Build visible nodes list based on expanded state
|
||||
const buildVisibleNodes = useCallback(() => {
|
||||
const visibleNodes: Array<{ entry: FileTreeEntry; depth: number }> = [];
|
||||
|
||||
for (const entry of entries) {
|
||||
const isExpanded = state.expandedPaths.has(entry.path);
|
||||
const isLoading = state.loadingPaths.has(entry.path);
|
||||
const error = state.errors.get(entry.path);
|
||||
const hasChildren = entry.type === 'directory';
|
||||
const processNode = (entry: FileTreeEntry, depth: number) => {
|
||||
visibleNodes.push({ entry, depth });
|
||||
|
||||
// Render the node
|
||||
nodes.push(
|
||||
<FileTreeNode
|
||||
key={entry.path}
|
||||
entry={entry}
|
||||
depth={depth}
|
||||
isExpanded={isExpanded}
|
||||
isLoading={isLoading}
|
||||
hasChildren={hasChildren}
|
||||
error={error}
|
||||
onToggle={toggleExpand}
|
||||
onSelect={handleFileSelect}
|
||||
/>
|
||||
);
|
||||
|
||||
// If directory is expanded, render children immediately after
|
||||
if (hasChildren && isExpanded && state.directoryCache.has(entry.path)) {
|
||||
const children = buildTree(state.directoryCache.get(entry.path)!, depth + 1);
|
||||
nodes.push(...children);
|
||||
// If this is an expanded directory with cached children, process them
|
||||
if (entry.type === 'directory' &&
|
||||
state.expandedPaths.has(entry.path) &&
|
||||
state.directoryCache.has(entry.path)) {
|
||||
const children = state.directoryCache.get(entry.path)!;
|
||||
for (const child of children) {
|
||||
processNode(child, depth + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return nodes;
|
||||
}, [state.expandedPaths, state.loadingPaths, state.errors, state.directoryCache, toggleExpand, handleFileSelect]);
|
||||
// Process root entries
|
||||
const rootEntries = state.directoryCache.get('') || [];
|
||||
for (const entry of rootEntries) {
|
||||
processNode(entry, 0);
|
||||
}
|
||||
|
||||
return visibleNodes;
|
||||
}, [state.expandedPaths, state.directoryCache]);
|
||||
|
||||
const rootEntries = state.directoryCache.get('') || [];
|
||||
const rootError = state.errors.get('');
|
||||
@ -200,7 +192,26 @@ export default function FileTree({ workspaceMode, onFileSelect }: FileTreeProps)
|
||||
)}
|
||||
|
||||
<div className="space-y-0.5">
|
||||
{buildTree(rootEntries)}
|
||||
{buildVisibleNodes().map(({ entry, depth }) => {
|
||||
const isExpanded = state.expandedPaths.has(entry.path);
|
||||
const isLoading = state.loadingPaths.has(entry.path);
|
||||
const error = state.errors.get(entry.path);
|
||||
const hasChildren = entry.type === 'directory';
|
||||
|
||||
return (
|
||||
<FileTreeNode
|
||||
key={entry.path}
|
||||
entry={entry}
|
||||
depth={depth}
|
||||
isExpanded={isExpanded}
|
||||
isLoading={isLoading}
|
||||
hasChildren={hasChildren}
|
||||
error={error}
|
||||
onToggle={toggleExpand}
|
||||
onSelect={handleFileSelect}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user