mirror of
https://github.com/potato47/ccc-devtools.git
synced 2026-04-06 05:12:28 +00:00
chore: update version to 1.0.3 and enhance TreePanel with expandable nodes and improved search functionality
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cccdev",
|
"name": "cccdev",
|
||||||
"version": "1.0.2",
|
"version": "1.0.3",
|
||||||
"description": "CLI tool for installing Cocos Creator devtools preview template",
|
"description": "CLI tool for installing Cocos Creator devtools preview template",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": "potato47",
|
"author": "potato47",
|
||||||
|
|||||||
@@ -66,6 +66,16 @@ function expandAncestors(nodes: TreeNode[], query: string): Set<string> {
|
|||||||
return toExpand;
|
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 }) {
|
function HighlightText({ text, query }: { text: string; query: string }) {
|
||||||
if (!query) return <span class="tree-label">{text}</span>;
|
if (!query) return <span class="tree-label">{text}</span>;
|
||||||
@@ -80,23 +90,54 @@ function HighlightText({ text, query }: { text: string; query: string }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 搜索结果行 ────────────────────────────────────────────
|
// ── 搜索结果行(支持展开/折叠子节点)─────────────────────
|
||||||
function SearchResultItem({ node, query }: { node: TreeNode; 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 isSelected = useComputed(() => selectedNode.value?.uuid === node.uuid);
|
||||||
|
const hasChildren = node.children.length > 0;
|
||||||
|
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
const ccNode = resolveNodeByPath(node.path);
|
const ccNode = resolveNodeByPath(node.path);
|
||||||
selectedNode.value = ccNode ?? null;
|
selectedNode.value = ccNode ?? null;
|
||||||
}, [node.path]);
|
}, [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 (
|
return (
|
||||||
|
<div class="tree-node">
|
||||||
<div
|
<div
|
||||||
class={`tree-row${isSelected.value ? ' selected' : ''}${!node.active ? ' inactive' : ''}`}
|
class={`tree-row${isSelected.value ? ' selected' : ''}${!node.active ? ' inactive' : ''}`}
|
||||||
style={{ paddingLeft: '8px' }}
|
style={{ paddingLeft: '8px' }}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
|
<span
|
||||||
|
class={`tree-arrow${hasChildren ? '' : ' invisible'}${expanded.value ? ' expanded' : ''}`}
|
||||||
|
onClick={handleToggle}
|
||||||
|
>
|
||||||
|
›
|
||||||
|
</span>
|
||||||
<HighlightText text={node.name} query={query} />
|
<HighlightText text={node.name} query={query} />
|
||||||
</div>
|
</div>
|
||||||
|
{hasChildren && expanded.value && (
|
||||||
|
<div class="tree-children">
|
||||||
|
{node.children.map((child) => (
|
||||||
|
<TreeNodeItem key={child.uuid} node={child} depth={1} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,17 +254,40 @@ export function TreePanel() {
|
|||||||
searchQuery.value = (e.target as HTMLInputElement).value;
|
searchQuery.value = (e.target as HTMLInputElement).value;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleClear = useCallback(() => {
|
/** 清空搜索并定位到当前选中节点 */
|
||||||
|
const clearAndLocate = useCallback(() => {
|
||||||
|
const selected = selectedNode.value;
|
||||||
searchQuery.value = '';
|
searchQuery.value = '';
|
||||||
inputRef.current?.focus();
|
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 清空搜索
|
// Esc 清空搜索并定位
|
||||||
const handleKeyDown = useCallback((e: KeyboardEvent) => {
|
const handleKeyDown = useCallback(
|
||||||
|
(e: KeyboardEvent) => {
|
||||||
if (e.key === 'Escape') {
|
if (e.key === 'Escape') {
|
||||||
searchQuery.value = '';
|
clearAndLocate();
|
||||||
}
|
}
|
||||||
}, []);
|
},
|
||||||
|
[clearAndLocate],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="tree-panel">
|
<div class="tree-panel">
|
||||||
@@ -242,7 +306,7 @@ export function TreePanel() {
|
|||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
/>
|
/>
|
||||||
{query.value && (
|
{query.value && (
|
||||||
<button class="tree-search-clear" onClick={handleClear} title="清空">
|
<button class="tree-search-clear" onClick={clearAndLocate} title="清空">
|
||||||
✕
|
✕
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user