fix(sidebar): preserve objects for ancestor search matches
Closes #5613
This commit is contained in:
1 parent
cce36ae058
commit
e0a7bc996d
9 files changed
+282
-41
No files matched your search
@@ -4,7 +4,13 @@ import { objectTypesForGroupNode } from "@/lib/table/tableTree";
|
||||
|
||||
export interface SidebarObjectGroupLoaders {
|
||||
loadTriggers(connectionId: string, database: string, table: string, schema?: string, nodeId?: string, catalog?: string): Promise<void>;
|
||||
loadObjectGroupChildren(node: TreeNode): Promise<void>;
|
||||
loadObjectGroupChildren(node: TreeNode, options?: SidebarObjectGroupLoadOptions): Promise<void>;
|
||||
}
|
||||
|
||||
export interface SidebarObjectGroupLoadOptions {
|
||||
searchFilter?: string;
|
||||
allowGlobalSearchMismatch?: boolean;
|
||||
expectedSidebarSearchQuery?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -12,13 +18,13 @@ export interface SidebarObjectGroupLoaders {
|
||||
* Table trigger groups are special because they are scoped to one table;
|
||||
* schema-level trigger groups are regular database object groups.
|
||||
*/
|
||||
export async function loadSidebarObjectGroup(node: TreeNode, loaders: SidebarObjectGroupLoaders): Promise<boolean> {
|
||||
export async function loadSidebarObjectGroup(node: TreeNode, loaders: SidebarObjectGroupLoaders, options?: SidebarObjectGroupLoadOptions): Promise<boolean> {
|
||||
if (node.type === "group-triggers" && node.connectionId && hasTreeNodeDatabaseContext(node) && node.tableName) {
|
||||
await loaders.loadTriggers(node.connectionId, node.database, node.tableName, node.schema, node.id, node.catalog);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!objectTypesForGroupNode(node.type)) return false;
|
||||
await loaders.loadObjectGroupChildren(node);
|
||||
await loaders.loadObjectGroupChildren(node, options);
|
||||
return true;
|
||||
}
|
||||
@@ -22,6 +22,36 @@ function normalizedLabel(node: TreeNode): string {
|
||||
return node.label;
|
||||
}
|
||||
|
||||
function findNodePath(nodes: readonly TreeNode[], targetNodeId: string, ancestors: readonly TreeNode[] = []): readonly TreeNode[] | undefined {
|
||||
for (const node of nodes) {
|
||||
const path = [...ancestors, node];
|
||||
if (node.id === targetNodeId) return path;
|
||||
if (node.children) {
|
||||
const childPath = findNodePath(node.children, targetNodeId, path);
|
||||
if (childPath) return childPath;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function nodePreservesSearchSubtree(node: TreeNode, matchLabel: SidebarLabelMatcher, searchableNodeTypes?: ReadonlySet<TreeNodeType>): boolean {
|
||||
if (searchableNodeTypes && !searchableNodeTypes.has(node.type)) return false;
|
||||
return preserveMatchedSubtreeTypes.has(node.type) && !!bestMatch(matchLabel, normalizedLabel(node), node.comment, node.searchAliases);
|
||||
}
|
||||
|
||||
export function createSidebarSearchSubtreePreserver(query: string, searchableNodeTypes?: ReadonlySet<TreeNodeType>): (node: TreeNode) => boolean {
|
||||
const matchLabel = query ? createSidebarLabelMatcher(query) : undefined;
|
||||
return (node) => !!matchLabel && nodePreservesSearchSubtree(node, matchLabel, searchableNodeTypes);
|
||||
}
|
||||
|
||||
export function resolveSidebarObjectSearchFilter(nodes: readonly TreeNode[], targetNodeId: string, query: string, searchableNodeTypes?: ReadonlySet<TreeNodeType>): string {
|
||||
if (!query) return query;
|
||||
const matchLabel = createSidebarLabelMatcher(query);
|
||||
const path = findNodePath(nodes, targetNodeId);
|
||||
const preservesSubtree = path?.some((node) => nodePreservesSearchSubtree(node, matchLabel, searchableNodeTypes));
|
||||
return preservesSubtree ? "" : query;
|
||||
}
|
||||
|
||||
export function filterSidebarTree(nodes: TreeNode[], query: string, collapsedIds: ReadonlySet<string>, searchableNodeTypes?: ReadonlySet<TreeNodeType>): TreeNode[] {
|
||||
const matchLabel = query ? createSidebarLabelMatcher(query) : undefined;
|
||||
if (!matchLabel && searchableNodeTypes === undefined) return nodes;
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { TreeNode } from "@/types/database";
|
||||
export interface SidebarTreeContext {
|
||||
getVisibleNodes: () => TreeNode[];
|
||||
getVisibleNodeIndex: (id: string) => number;
|
||||
getTreeLoadSearchOptions?: (node: TreeNode) => { searchFilter: string; allowGlobalSearchMismatch: true; expectedSidebarSearchQuery: string } | undefined;
|
||||
setTableSearchQuery?: (parentNodeId: string, query: string, local: boolean) => void;
|
||||
refreshTableSearchIndex?: (parentNodeId: string) => void;
|
||||
registerPasteHandler?: (nodeId: string, callback: () => void) => () => void;
|
||||
|
||||
Reference in new issue
Block a user