diff --git a/package.json b/package.json index 8600128..4ba3597 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cccdev", - "version": "1.0.2", + "version": "1.0.3", "description": "CLI tool for installing Cocos Creator devtools preview template", "license": "MIT", "author": "potato47", diff --git a/packages/cccdev-template-3x/src/components/TreePanel.tsx b/packages/cccdev-template-3x/src/components/TreePanel.tsx index 47e4310..5239e7e 100644 --- a/packages/cccdev-template-3x/src/components/TreePanel.tsx +++ b/packages/cccdev-template-3x/src/components/TreePanel.tsx @@ -66,6 +66,16 @@ function expandAncestors(nodes: TreeNode[], query: string): Set { return toExpand; } +// ── 在树中查找目标节点的 uuid 路径 ─────────────────────── +function findPathInTree(nodes: TreeNode[], targetUuid: string): string[] | undefined { + for (const node of nodes) { + if (node.uuid === targetUuid) return [node.uuid]; + const childPath = findPathInTree(node.children, targetUuid); + if (childPath) return [node.uuid, ...childPath]; + } + return undefined; +} + // ── 高亮关键词 ──────────────────────────────────────────── function HighlightText({ text, query }: { text: string; query: string }) { if (!query) return {text}; @@ -80,22 +90,53 @@ function HighlightText({ text, query }: { text: string; query: string }) { ); } -// ── 搜索结果行 ──────────────────────────────────────────── +// ── 搜索结果行(支持展开/折叠子节点)───────────────────── function SearchResultItem({ node, query }: { node: TreeNode; query: string }) { + const expanded = useComputed(() => expandedUuids.value.has(node.uuid)); const isSelected = useComputed(() => selectedNode.value?.uuid === node.uuid); + const hasChildren = node.children.length > 0; const handleClick = useCallback(() => { const ccNode = resolveNodeByPath(node.path); selectedNode.value = ccNode ?? null; }, [node.path]); + const handleToggle = useCallback( + (e: MouseEvent) => { + e.stopPropagation(); + const next = new Set(expandedUuids.value); + if (next.has(node.uuid)) { + next.delete(node.uuid); + } else { + next.add(node.uuid); + } + expandedUuids.value = next; + }, + [node.uuid], + ); + return ( -
- +
+
+ + › + + +
+ {hasChildren && expanded.value && ( +
+ {node.children.map((child) => ( + + ))} +
+ )}
); } @@ -213,17 +254,40 @@ export function TreePanel() { searchQuery.value = (e.target as HTMLInputElement).value; }, []); - const handleClear = useCallback(() => { + /** 清空搜索并定位到当前选中节点 */ + const clearAndLocate = useCallback(() => { + const selected = selectedNode.value; searchQuery.value = ''; inputRef.current?.focus(); + + if (!selected) return; + + // 在树中找到选中节点的 path,展开所有祖先 + const path: string[] | undefined = findPathInTree(treeData.value, selected.uuid); + if (path && path.length > 1) { + const next = new Set(expandedUuids.value); + // 展开除最后一个(自身)之外的所有祖先 + for (let i = 0; i < path.length - 1; i++) next.add(path[i]); + expandedUuids.value = next; + } + + // 等 DOM 更新后滚动到选中节点 + requestAnimationFrame(() => { + const scrollContainer = inputRef.current?.closest('.tree-panel')?.querySelector('.tree-scroll'); + const el = scrollContainer?.querySelector('.tree-row.selected'); + el?.scrollIntoView({ block: 'center', behavior: 'smooth' }); + }); }, []); - // Esc 清空搜索 - const handleKeyDown = useCallback((e: KeyboardEvent) => { - if (e.key === 'Escape') { - searchQuery.value = ''; - } - }, []); + // Esc 清空搜索并定位 + const handleKeyDown = useCallback( + (e: KeyboardEvent) => { + if (e.key === 'Escape') { + clearAndLocate(); + } + }, + [clearAndLocate], + ); return (
@@ -242,7 +306,7 @@ export function TreePanel() { onKeyDown={handleKeyDown} /> {query.value && ( - )}