fix(sidebar): 修复搜索筛选树分组折叠在 scope-only 过滤下空操作

* 🐛 fix(sidebar): 修复搜索筛选树的分组折叠与重复点击切换

- 在保留搜索子树时应用临时折叠状态,避免空分组重新展开
- 仅在搜索筛选期间记录节点折叠操作
- 允许分组标签双击后的连续点击正常切换展开状态
- 补充侧边栏搜索及节点点击行为测试

* fix(sidebar): collapseAllTreeNodes 按 scope-only 过滤填充 searchCollapsedIds

---------

Co-authored-by: t8y2 <t8y2@users.noreply.github.com>
Co-authored-by: skyler <1156263951@qq.com>
This commit is contained in:
authored and GitHub committed 2026-08-06 00:55:24 +08:00
1 parent b58c221e16
commit fa6540b739
7 files changed
+66 -8

No files matched your search

@@ -33,6 +33,19 @@ export function reuseLiveSidebarTreeNodes(indexedNodes: TreeNode[], liveNodes: r
return indexedNodes.map((node) => liveNodesById.get(node.id) ?? node);
}
function applySearchCollapsedState(node: TreeNode, collapsedIds: ReadonlySet<string>): TreeNode {
const children = node.children?.map((child) => applySearchCollapsedState(child, collapsedIds));
const childrenChanged = children?.some((child, index) => child !== node.children?.[index]) ?? false;
const collapsed = collapsedIds.has(node.id);
if (!collapsed && !childrenChanged) return node;
return {
...node,
children: childrenChanged ? children : node.children,
isExpanded: collapsed ? false : node.isExpanded,
};
}
function filterSidebarTreeWithMatcher(nodes: TreeNode[], matchLabel: SidebarLabelMatcher | undefined, collapsedIds: ReadonlySet<string>, searchableNodeTypes?: ReadonlySet<TreeNodeType>): TreeNode[] {
const filteredNodes: { node: TreeNode; score: number }[] = [];
@@ -57,7 +70,7 @@ function filterSidebarTreeWithMatcher(nodes: TreeNode[], matchLabel: SidebarLabe
// A type-matched table keeps its loaded detail groups after the text query
// is cleared instead of being rebuilt with an empty filtered child list.
const preservesTypeMatchedTable = !matchLabel && !!selfMatch && node.type === "table";
const filteredChildren = preservesSubtree ? node.children : node.children ? filterSidebarTreeWithMatcher(node.children, matchLabel, collapsedIds, searchableNodeTypes) : undefined;
const filteredChildren = preservesSubtree ? node.children?.map((child) => applySearchCollapsedState(child, collapsedIds)) : node.children ? filterSidebarTreeWithMatcher(node.children, matchLabel, collapsedIds, searchableNodeTypes) : undefined;
if (selfMatch || (filteredChildren && filteredChildren.length > 0)) {
if (!node.children || preservesTypeMatchedTable) {
@@ -43,7 +43,8 @@ export function treeNodeRowAction(type: TreeNodeType, canExpand: boolean, activa
return "none";
}
export function shouldRunTreeNodeRowAction(action: TreeNodeRowAction, clickDetail: number): boolean {
export function shouldRunTreeNodeRowAction(action: TreeNodeRowAction, clickDetail: number, allowRepeatedToggle = false): boolean {
if (action === "toggle" && allowRepeatedToggle) return true;
// Double-clicks emit a second click before dblclick; leave that event to the
// double-click handler so expandable database rows do not toggle first.
return action !== "none" && clickDetail <= 1;