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]);
|
}, [onFileSelect]);
|
||||||
|
|
||||||
// Build tree structure from flat entries
|
// Build visible nodes list based on expanded state
|
||||||
const buildTree = useCallback((entries: FileTreeEntry[], depth: number = 0) => {
|
const buildVisibleNodes = useCallback(() => {
|
||||||
const nodes: JSX.Element[] = [];
|
const visibleNodes: Array<{ entry: FileTreeEntry; depth: number }> = [];
|
||||||
|
|
||||||
for (const entry of entries) {
|
const processNode = (entry: FileTreeEntry, depth: number) => {
|
||||||
const isExpanded = state.expandedPaths.has(entry.path);
|
visibleNodes.push({ entry, depth });
|
||||||
const isLoading = state.loadingPaths.has(entry.path);
|
|
||||||
const error = state.errors.get(entry.path);
|
// If this is an expanded directory with cached children, process them
|
||||||
const hasChildren = entry.type === 'directory';
|
if (entry.type === 'directory' &&
|
||||||
|
state.expandedPaths.has(entry.path) &&
|
||||||
// Render the node
|
state.directoryCache.has(entry.path)) {
|
||||||
nodes.push(
|
const children = state.directoryCache.get(entry.path)!;
|
||||||
<FileTreeNode
|
for (const child of children) {
|
||||||
key={entry.path}
|
processNode(child, depth + 1);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Process root entries
|
||||||
|
const rootEntries = state.directoryCache.get('') || [];
|
||||||
|
for (const entry of rootEntries) {
|
||||||
|
processNode(entry, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodes;
|
return visibleNodes;
|
||||||
}, [state.expandedPaths, state.loadingPaths, state.errors, state.directoryCache, toggleExpand, handleFileSelect]);
|
}, [state.expandedPaths, state.directoryCache]);
|
||||||
|
|
||||||
const rootEntries = state.directoryCache.get('') || [];
|
const rootEntries = state.directoryCache.get('') || [];
|
||||||
const rootError = state.errors.get('');
|
const rootError = state.errors.get('');
|
||||||
@ -200,7 +192,26 @@ export default function FileTree({ workspaceMode, onFileSelect }: FileTreeProps)
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="space-y-0.5">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user