mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-30 00:19:35 +00:00
124 lines
3.6 KiB
TypeScript
124 lines
3.6 KiB
TypeScript
import { SIDEBAR_TREE_ROW_HEIGHT, type FlatTreeNode } from "@/composables/useFlatTree";
|
|
import type { QueryTab, TreeNode } from "@/types/database";
|
|
|
|
export type ActiveTabSidebarTarget =
|
|
| {
|
|
type: "table";
|
|
connectionId: string;
|
|
database: string;
|
|
schema?: string;
|
|
tableName: string;
|
|
}
|
|
| {
|
|
type: "mongo-collection";
|
|
connectionId: string;
|
|
database: string;
|
|
collectionName: string;
|
|
}
|
|
| {
|
|
type: "saved-sql-file";
|
|
savedSqlId: string;
|
|
};
|
|
|
|
export function activeTabSidebarTarget(tab: QueryTab | undefined | null): ActiveTabSidebarTarget | null {
|
|
if (!tab) return null;
|
|
|
|
if (tab.savedSqlId) {
|
|
return { type: "saved-sql-file", savedSqlId: tab.savedSqlId };
|
|
}
|
|
|
|
if (tab.mode === "data") {
|
|
const tableName = tab.tableMeta?.tableName || tab.title;
|
|
if (!tableName) return null;
|
|
return {
|
|
type: "table",
|
|
connectionId: tab.connectionId,
|
|
database: tab.database,
|
|
schema: tab.tableMeta?.schema ?? tab.schema,
|
|
tableName,
|
|
};
|
|
}
|
|
|
|
if (tab.mode === "mongo") {
|
|
const collectionName = tab.sql || tab.title.split(".").pop() || tab.title;
|
|
if (!collectionName) return null;
|
|
return {
|
|
type: "mongo-collection",
|
|
connectionId: tab.connectionId,
|
|
database: tab.database,
|
|
collectionName,
|
|
};
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function schemaMatches(node: TreeNode, schema: string | undefined): boolean {
|
|
if (!schema) return true;
|
|
return (node.schema || "") === schema;
|
|
}
|
|
|
|
function matchesTarget(node: TreeNode, target: ActiveTabSidebarTarget): boolean {
|
|
if (target.type === "saved-sql-file") {
|
|
return node.type === "saved-sql-file" && node.savedSqlId === target.savedSqlId;
|
|
}
|
|
|
|
if (target.type === "mongo-collection") {
|
|
return (
|
|
node.type === "mongo-collection" &&
|
|
node.connectionId === target.connectionId &&
|
|
node.database === target.database &&
|
|
node.label === target.collectionName
|
|
);
|
|
}
|
|
|
|
return (
|
|
(node.type === "table" || node.type === "view") &&
|
|
node.connectionId === target.connectionId &&
|
|
node.database === target.database &&
|
|
schemaMatches(node, target.schema) &&
|
|
node.label === target.tableName
|
|
);
|
|
}
|
|
|
|
export function findSidebarNodeForActiveTab(
|
|
tab: QueryTab | undefined | null,
|
|
flatNodes: readonly FlatTreeNode[],
|
|
): FlatTreeNode | null {
|
|
const target = activeTabSidebarTarget(tab);
|
|
if (!target) return null;
|
|
return flatNodes.find((item) => matchesTarget(item.node, target)) ?? null;
|
|
}
|
|
|
|
export function shouldScrollActiveSidebarSelection(options: {
|
|
activeTabId: string | null | undefined;
|
|
previousActiveTabId: string | null | undefined;
|
|
autoSelectEnabled: boolean;
|
|
previousAutoSelectEnabled: boolean | undefined;
|
|
}): boolean {
|
|
if (!options.autoSelectEnabled) return false;
|
|
return (
|
|
options.activeTabId !== options.previousActiveTabId ||
|
|
(options.autoSelectEnabled && options.previousAutoSelectEnabled === false)
|
|
);
|
|
}
|
|
|
|
export function scrollTopForSidebarNode(options: {
|
|
index: number;
|
|
currentScrollTop: number;
|
|
viewportHeight: number;
|
|
rowHeight?: number;
|
|
}): number {
|
|
const rowHeight = options.rowHeight ?? SIDEBAR_TREE_ROW_HEIGHT;
|
|
if (options.index < 0 || options.viewportHeight <= 0) return options.currentScrollTop;
|
|
|
|
const rowTop = options.index * rowHeight;
|
|
const rowBottom = rowTop + rowHeight;
|
|
const viewportTop = options.currentScrollTop;
|
|
const viewportBottom = options.currentScrollTop + options.viewportHeight;
|
|
|
|
if (rowTop < viewportTop) return rowTop;
|
|
if (rowBottom > viewportBottom) return Math.max(0, rowBottom - options.viewportHeight);
|
|
return options.currentScrollTop;
|
|
}
|