fix(sidebar): separate root connection filtering
This commit is contained in:
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)"
|
||||
|
||||
Reference in new issue
Block a user