refactor: sync file tree open state

This commit is contained in:
yyh
2026-01-15 16:35:10 +08:00
parent 2f92957e15
commit eab395f58a
2 changed files with 39 additions and 22 deletions

View File

@ -1,6 +1,7 @@
'use client'
import type { NodeApi, TreeApi } from 'react-arborist'
import type { OpensObject } from './store'
import type { TreeNodeData } from './type'
import { RiDragDropLine } from '@remixicon/react'
import * as React from 'react'
@ -15,7 +16,7 @@ import { cn } from '@/utils/classnames'
import FileTreeContextMenu from './file-tree-context-menu'
import FileTreeNode from './file-tree-node'
import { useSkillEditorStore, useSkillEditorStoreApi } from './store'
import { getAncestorIds, toOpensObject } from './utils/tree-utils'
import { getAncestorIds } from './utils/tree-utils'
type FilesProps = {
className?: string
@ -48,7 +49,11 @@ const Files: React.FC<FilesProps> = ({ className }) => {
const renameNode = useRenameAppAssetNode()
const initialOpenState = useMemo(() => toOpensObject(expandedFolderIds), [expandedFolderIds])
const initialOpensObject = useMemo<OpensObject>(() => {
return Object.fromEntries(
[...expandedFolderIds].map(id => [id, true]),
)
}, [expandedFolderIds])
const handleToggle = useCallback((id: string) => {
storeApi.getState().toggleFolder(id)
@ -75,31 +80,23 @@ const Files: React.FC<FilesProps> = ({ className }) => {
}, [appId, renameNode, t])
useEffect(() => {
if (!activeTabId || !treeData?.children || !treeRef.current)
if (!activeTabId || !treeData?.children)
return
const tree = treeRef.current
if (!tree)
return
const ancestors = getAncestorIds(activeTabId, treeData.children)
if (ancestors.length > 0)
storeApi.getState().revealFile(ancestors)
const timeoutId = setTimeout(() => {
const tree = treeRef.current
if (!tree)
return
for (const ancestorId of ancestors) {
const ancestorNode = tree.get(ancestorId)
if (ancestorNode && !ancestorNode.isOpen)
ancestorNode.open()
}
requestAnimationFrame(() => {
const node = tree.get(activeTabId)
if (node)
node.select()
}, 0)
return () => clearTimeout(timeoutId)
if (node) {
tree.openParents(node)
tree.scrollTo(activeTabId)
}
})
}, [activeTabId, treeData?.children, storeApi])
if (isLoading) {
@ -146,8 +143,8 @@ const Files: React.FC<FilesProps> = ({ className }) => {
rowHeight={24}
indent={20}
overscanCount={5}
initialOpenState={initialOpenState}
selection={activeTabId ?? undefined}
initialOpenState={initialOpensObject}
onToggle={handleToggle}
onActivate={handleActivate}
onRename={handleRename}

View File

@ -65,11 +65,15 @@ export const createTabSlice: StateCreator<TabSliceShape> = (set, get) => ({
},
})
export type OpensObject = Record<string, boolean>
export type FileTreeSliceShape = {
expandedFolderIds: Set<string>
setExpandedFolderIds: (ids: Set<string>) => void
toggleFolder: (folderId: string) => void
revealFile: (ancestorFolderIds: string[]) => void
setExpandedFromOpens: (opens: OpensObject) => void
getOpensObject: () => OpensObject
}
export const createFileTreeSlice: StateCreator<FileTreeSliceShape> = (set, get) => ({
@ -96,6 +100,22 @@ export const createFileTreeSlice: StateCreator<FileTreeSliceShape> = (set, get)
ancestorFolderIds.forEach(id => newSet.add(id))
set({ expandedFolderIds: newSet })
},
setExpandedFromOpens: (opens: OpensObject) => {
const newSet = new Set<string>(
Object.entries(opens)
.filter(([_, isOpen]) => isOpen)
.map(([id]) => id),
)
set({ expandedFolderIds: newSet })
},
getOpensObject: () => {
const { expandedFolderIds } = get()
return Object.fromEntries(
[...expandedFolderIds].map(id => [id, true]),
)
},
})
export type DirtySliceShape = {