fix(sidebar): separate root connection filtering

This commit is contained in:
二丫讲梵 authored and GitHub committed 2026-07-23 17:29:52 +08:00
1 parent ad2d52e3ca
commit b9c1feab82
11 files changed
+158 -11

No files matched your search

@@ -1,13 +1,13 @@
<script setup lang="ts">
import { ref, shallowRef, computed, nextTick, watch, provide, onMounted, onUnmounted, type Component, type ComponentPublicInstance, type CSSProperties } from "vue";
import { useI18n } from "vue-i18n";
import { Search, X, ListFilter, ListOrdered, ArrowDownAZ, ArrowUpZA, Crosshair, Server, Database, FolderTree, Table2, Eye, RotateCcw } from "@lucide/vue";
import { Search, X, ListFilter, ListOrdered, ArrowDownAZ, ArrowUpZA, CircleDot, Crosshair, Server, Database, FolderTree, Table2, Eye, RotateCcw } from "@lucide/vue";
import { useConnectionStore } from "@/stores/connectionStore";
import { useQueryStore } from "@/stores/queryStore";
import { useSettingsStore } from "@/stores/settingsStore";
import { useToast } from "@/composables/useToast";
import type { ObjectSourceKind, TreeNode, TreeNodeType } from "@/types/database";
import { filterSidebarSearchRootsByConnectionState, filterSidebarTree } from "@/lib/sidebar/sidebarSearchTree";
import { filterSidebarSearchRootsByConnectionState, filterSidebarTree, filterSidebarTreeToConnectedConnections, resolveSidebarFilterGuards } from "@/lib/sidebar/sidebarSearchTree";
import { isCancelSearchShortcut, isCopySidebarSelectionShortcut, isEditSidebarConnectionShortcut, isPasteSidebarSelectionShortcut } from "@/lib/editor/keyboardShortcuts";
import { copyNameForTreeNode, objectSourceKindForTreeNode } from "@/lib/sidebar/treeNodeClick";
import { copyToClipboard } from "@/lib/common/clipboard";
@@ -49,6 +49,7 @@ const settingsStore = useSettingsStore();
const { toast } = useToast();
const searchQuery = ref("");
const deferredSearchQuery = ref("");
const showConnectedConnectionsOnly = ref(false);
const searchInputRef = ref<HTMLInputElement>();
const rootRef = ref<HTMLElement>();
const pointerInsideTree = ref(false);
@@ -118,7 +119,7 @@ watch(
);
function refreshActiveSidebarTableSearches() {
if (isFiltering.value) return;
if (isTreeSearchFiltering.value) return;
for (const parentNodeId of Object.keys(store.sidebarTableSearchQueries)) {
scheduleSidebarTableSearchRefresh(parentNodeId);
}
@@ -200,7 +201,11 @@ function collectExpandedObjectSearchTargets(node: TreeNode, tasks: Promise<void>
}
const isSearching = computed(() => !!deferredSearchQuery.value);
const isFiltering = computed(() => !!searchQuery.value.trim() || hasSearchScopeFilter.value);
const sidebarFilterGuards = computed(() => resolveSidebarFilterGuards(showConnectedConnectionsOnly.value, searchQuery.value, hasSearchScopeFilter.value));
// Connected-only filtering changes only root visibility, so descendant-local
// features stay available while operations requiring the full root list pause.
const isTreeSearchFiltering = computed(() => sidebarFilterGuards.value.isTreeSearchFiltering);
const isRootListPartial = computed(() => sidebarFilterGuards.value.isRootListPartial);
const SEARCH_SCOPE_TO_NODE_TYPES: Record<SearchScope, TreeNodeType[]> = {
connection: ["connection"],
@@ -300,7 +305,7 @@ function clearSearchScopeFilter() {
function scheduleSidebarTableSearchRefresh(parentNodeId: string, options?: { restoreFocus?: boolean }) {
window.clearTimeout(tableSearchTimers.get(parentNodeId));
if (isFiltering.value) return;
if (isTreeSearchFiltering.value) return;
const restoreToken = options?.restoreFocus ? ++tableSearchFocusRestoreTokenSeq : 0;
if (restoreToken) {
tableSearchFocusRestoreTokens.clear();
@@ -343,6 +348,9 @@ const displayedTreeNodes = computed(() => sortConnectionListForDisplay(store.tre
const filteredNodes = computed(() => {
let nodes = displayedTreeNodes.value;
if (showConnectedConnectionsOnly.value) {
nodes = filterSidebarTreeToConnectedConnections(nodes, store.connectedIds);
}
const q = deferredSearchQuery.value;
nodes = filterSidebarTree(nodes, q, searchCollapsedIds.value, searchableNodeTypes.value);
@@ -355,7 +363,7 @@ const filteredNodes = computed(() => {
const flatNodes = computed<FlatTreeNode[]>(() =>
insertSidebarTableSearchControls(flattenTree(filteredNodes.value), {
enabled: settingsStore.editorSettings.sidebarTableSearchEnabled && !isFiltering.value,
enabled: settingsStore.editorSettings.sidebarTableSearchEnabled && !isTreeSearchFiltering.value,
sidebarObjectDisplay: settingsStore.editorSettings.sidebarObjectDisplay,
activeQueries: store.sidebarTableSearchQueries,
}),
@@ -512,7 +520,7 @@ watch(
);
const stickyNode = computed<FlatTreeNode | null>(() => {
if (!useVirtualTree.value || isFiltering.value) return null;
if (!useVirtualTree.value || isTreeSearchFiltering.value) return null;
const nodes = flatNodes.value;
const len = nodes.length;
if (len === 0) return null;
@@ -717,9 +725,10 @@ async function createNewGroup() {
async function startRenamingCreatedGroup(groupId: string) {
pendingRenameGroupId.value = groupId;
store.selectedTreeNodeId = groupId;
if (isFiltering.value) {
if (isRootListPartial.value) {
searchQuery.value = "";
deferredSearchQuery.value = "";
showConnectedConnectionsOnly.value = false;
clearSearchScopeFilter();
}
@@ -754,9 +763,10 @@ async function locateActiveTabInSidebar() {
await ensureTreeLoadedForTarget(initialTarget);
// Clear any active search filter so the node is visible
if (isFiltering.value) {
if (isRootListPartial.value) {
searchQuery.value = "";
deferredSearchQuery.value = "";
showConnectedConnectionsOnly.value = false;
clearSearchScopeFilter();
}
@@ -1450,6 +1460,17 @@ defineExpose({ focusSearch, createNewGroup, collapseAllTreeNodes });
align="end"
@update:model-value="selectSearchScopeMenuItem"
/>
<button
type="button"
class="shrink-0 h-6 w-6 flex items-center justify-center rounded border hover:bg-accent"
:class="showConnectedConnectionsOnly ? 'text-primary bg-primary/10 border-primary/30' : 'border-border text-muted-foreground hover:text-foreground'"
:aria-label="t('sidebar.showActiveConnectionsOnly')"
:aria-pressed="showConnectedConnectionsOnly"
:title="t('sidebar.showActiveConnectionsOnly')"
@click="showConnectedConnectionsOnly = !showConnectedConnectionsOnly"
>
<CircleDot class="h-3.5 w-3.5" />
</button>
</div>
</div>
<CustomContextMenu ref="sidebarContextMenuRef" :items="sidebarContextMenuItems" v-slot="contextMenuSlot">
@@ -1472,7 +1493,7 @@ defineExpose({ focusSearch, createNewGroup, collapseAllTreeNodes });
<TreeItem
:node="item.node"
:depth="item.depth"
:drag-disabled="isFiltering || isConnectionListAlphabeticallySorted"
:drag-disabled="isRootListPartial || isConnectionListAlphabeticallySorted"
:pending-rename="pendingRenameGroupId === item.node.id"
:highlighted="highlightedNodeId === item.node.id"
:comment-label-width="sidebarCommentLabelWidths.get(item.node.id)"
@@ -1496,7 +1517,7 @@ defineExpose({ focusSearch, createNewGroup, collapseAllTreeNodes });
:key="item.id"
:node="item.node"
:depth="item.depth"
:drag-disabled="isFiltering || isConnectionListAlphabeticallySorted"
:drag-disabled="isRootListPartial || isConnectionListAlphabeticallySorted"
:pending-rename="pendingRenameGroupId === item.node.id"
:highlighted="highlightedNodeId === item.id"
:comment-label-width="sidebarCommentLabelWidths.get(item.node.id)"