feat(sidebar): show connection group paths
This commit is contained in:
14 files changed
+251
-10
No files matched your search
@@ -14,6 +14,7 @@ import { useSchemaOptions } from "@/composables/useSchemaOptions";
|
||||
import { connectionIconType } from "@/lib/connection/connectionPresentation";
|
||||
import { formatDatabaseLabel, isDefaultDatabase } from "@/lib/database/defaultDatabase";
|
||||
import { connectionDisplayName } from "@/lib/tabs/tabPresentation";
|
||||
import { buildConnectionGroupPathMap } from "@/lib/sidebar/sidebarLayout";
|
||||
import { isSingleDatabase, supportsSqlInListPaste, supportsTransaction as supportsTransactionFeature } from "@/lib/database/databaseCapabilities";
|
||||
import { hexToRgba } from "@/lib/common/color";
|
||||
import { productionContextForDatabase } from "@/lib/database/productionSafety";
|
||||
@@ -66,6 +67,7 @@ const activeDatabaseOptions = computed(() => {
|
||||
});
|
||||
|
||||
const connectionOptionIds = computed(() => connectionStore.connections.map((connection) => connection.id));
|
||||
const connectionGroupPaths = computed(() => buildConnectionGroupPathMap(connectionStore.sidebarLayout));
|
||||
const activeDatabaseValue = computed(() => props.activeTab.database || "");
|
||||
const activeProductionContext = computed(() => productionContextForDatabase(props.activeConnection, props.activeTab.database));
|
||||
const showConnectionProductionBadge = computed(() => activeProductionContext.value.reason === "connection");
|
||||
@@ -154,6 +156,10 @@ function connectionById(connectionId: string): ConnectionConfig | undefined {
|
||||
return connectionStore.getConfig(connectionId);
|
||||
}
|
||||
|
||||
function connectionGroupLabel(connectionId: string): string {
|
||||
return connectionGroupPaths.value.get(connectionId)?.join(" / ") || t("connectionGroup.ungroupedLabel");
|
||||
}
|
||||
|
||||
function databaseOptionIsProduction(database: string): boolean {
|
||||
if (!database || props.activeConnection?.is_production) return false;
|
||||
return productionContextForDatabase(props.activeConnection, database).reason === "database";
|
||||
@@ -326,6 +332,8 @@ function databaseOptionIsProduction(database: string): boolean {
|
||||
:loading-text="t('common.loading')"
|
||||
trigger-class="font-medium text-foreground"
|
||||
:display-name="connectionDisplayName"
|
||||
list-class="w-96 max-w-[calc(100vw-2rem)]"
|
||||
item-class="h-9"
|
||||
@update:model-value="(connectionId) => emit('changeConnection', connectionId)"
|
||||
>
|
||||
<template #trigger-label="{ label }">
|
||||
@@ -339,7 +347,10 @@ function databaseOptionIsProduction(database: string): boolean {
|
||||
<template #option-label="{ option, label }">
|
||||
<div class="flex min-w-0 items-center gap-2">
|
||||
<DatabaseIcon :db-type="connectionIconType(connectionById(option))" class="h-3.5 w-3.5 shrink-0" />
|
||||
<TruncatedTextTooltip :text="label" class="min-w-0 flex-1" side="left" :side-offset="8" />
|
||||
<div class="flex min-w-0 flex-1 items-center gap-2">
|
||||
<TruncatedTextTooltip :text="connectionGroupLabel(option)" class="block min-w-0 max-w-48 shrink-0 rounded-sm bg-muted/70 px-1.5 py-0.5 text-[11px] text-muted-foreground" side="left" :side-offset="8" />
|
||||
<TruncatedTextTooltip :text="label" class="block min-w-[7rem] flex-1 text-sm font-medium" side="left" :side-offset="8" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</SearchableSelect>
|
||||
|
||||
@@ -27,6 +27,7 @@ const props = withDefaults(
|
||||
triggerClass?: HTMLAttributes["class"];
|
||||
triggerIconClass?: HTMLAttributes["class"];
|
||||
contentClass?: HTMLAttributes["class"];
|
||||
listClass?: HTMLAttributes["class"];
|
||||
itemClass?: HTMLAttributes["class"];
|
||||
displayName?: (option: string) => string;
|
||||
optionTooltip?: (option: string) => string | undefined;
|
||||
@@ -236,13 +237,13 @@ function handleKeydown(event: KeyboardEvent) {
|
||||
</PopoverTrigger>
|
||||
<PopoverContent :align="SEARCHABLE_SELECT_HELP_PANEL_ALIGN" :class="cn('w-auto max-w-[calc(100vw-1rem)] border-0 bg-transparent p-0 shadow-none ring-0', contentClass)">
|
||||
<div class="flex min-w-0 flex-col gap-2 sm:flex-row sm:items-start">
|
||||
<div ref="listCard" class="w-52 shrink-0 rounded-md border bg-popover p-1.5 shadow-md">
|
||||
<div ref="listCard" :class="cn('w-52 shrink-0 rounded-md border bg-popover p-1.5 shadow-md', listClass)">
|
||||
<div class="relative rounded-sm border bg-background">
|
||||
<Search class="pointer-events-none absolute left-2 top-1/2 h-3 w-3 -translate-y-1/2 text-muted-foreground" />
|
||||
<span v-if="!searchText" class="pointer-events-none absolute left-[25px] top-1/2 -translate-y-1/2 text-sm text-muted-foreground">{{ searchPlaceholder }}</span>
|
||||
<Input ref="searchInput" :model-value="searchText" class="h-6 border-0 pl-6 pr-2 text-sm caret-foreground shadow-none focus-visible:ring-0" @update:model-value="(value) => (searchText = String(value))" @keydown="handleKeydown" />
|
||||
</div>
|
||||
<div ref="listContainer" class="max-h-64 overflow-y-auto py-1" @scroll="updateHelpPanelOffset">
|
||||
<div ref="listContainer" class="dbx-searchable-select-list max-h-64 overflow-y-auto py-1" @scroll="updateHelpPanelOffset">
|
||||
<div v-if="loading" class="px-2 py-2 text-sm text-muted-foreground">
|
||||
{{ loadingText }}
|
||||
</div>
|
||||
@@ -348,4 +349,43 @@ function handleKeydown(event: KeyboardEvent) {
|
||||
border-color: rgb(147, 197, 253) !important;
|
||||
box-shadow: 0 0 0 2px rgba(147, 197, 253, 0.24) !important;
|
||||
}
|
||||
|
||||
.dbx-searchable-select-list {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: color-mix(in oklch, var(--foreground) 30%, transparent) transparent;
|
||||
}
|
||||
|
||||
.dbx-searchable-select-list::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.dbx-searchable-select-list::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.dbx-searchable-select-list::-webkit-scrollbar-thumb {
|
||||
border: 1px solid transparent;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in oklch, var(--foreground) 30%, transparent);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.dbx-searchable-select-list:hover::-webkit-scrollbar-thumb {
|
||||
border: 0;
|
||||
background: color-mix(in oklch, var(--foreground) 48%, transparent);
|
||||
}
|
||||
|
||||
.dark .dbx-searchable-select-list {
|
||||
scrollbar-color: rgb(82, 82, 91) transparent;
|
||||
}
|
||||
|
||||
.dark .dbx-searchable-select-list::-webkit-scrollbar-thumb {
|
||||
background: rgb(82, 82, 91);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.dark .dbx-searchable-select-list:hover::-webkit-scrollbar-thumb {
|
||||
background: rgb(113, 113, 122);
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user