feat(ui): streamline sidebar and settings layout
This commit is contained in:
21 files changed
+806
-429
No files matched your search
@@ -2,7 +2,7 @@
|
||||
import { computed, ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { translateBackendError } from "@/i18n/backend-errors";
|
||||
import { Upload, Download, FolderPlus, FolderOpen, RefreshCw, ChevronsLeft, ChevronsUp, Trash2, FolderInput, Check, Minus, Square, X } from "@lucide/vue";
|
||||
import { Upload, Download, ArrowDownUp, FolderPlus, FolderOpen, RefreshCw, ChevronsLeft, ChevronsDownUp, Trash2, FolderInput, Check, Minus, Square, X } from "@lucide/vue";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
@@ -45,6 +45,20 @@ const importSourceItems = computed(() => [
|
||||
{ value: "dbeaver", label: t("sidebar.importDbeaver") },
|
||||
{ value: "datagrip", label: t("sidebar.importDatagrip") },
|
||||
]);
|
||||
const connectionTransferItems = computed(() => [
|
||||
...importSourceItems.value.map((item) => ({
|
||||
...item,
|
||||
value: `import:${item.value}`,
|
||||
icon: Download,
|
||||
})),
|
||||
{
|
||||
value: "export",
|
||||
label: t("sidebar.export"),
|
||||
icon: Upload,
|
||||
separatorBefore: true,
|
||||
},
|
||||
]);
|
||||
const connectionTransferLabel = computed(() => t("sidebar.importExport"));
|
||||
const connectionIdSet = computed(() => new Set(connectionStore.connections.map((connection) => connection.id)));
|
||||
const allConnectionIds = computed(() => connectionStore.connections.map((connection) => connection.id));
|
||||
const selectedConnectionIds = computed(() => (connectionStore.connectionMultiSelectActive ? connectionStore.selectedTreeNodeIds.filter((id) => connectionIdSet.value.has(id)) : []));
|
||||
@@ -84,6 +98,14 @@ function selectImportSource(source: string) {
|
||||
emit("import", source as ImportSource);
|
||||
}
|
||||
|
||||
function selectConnectionTransferAction(action: string) {
|
||||
if (action === "export") {
|
||||
emit("export");
|
||||
return;
|
||||
}
|
||||
if (action.startsWith("import:")) selectImportSource(action.slice("import:".length));
|
||||
}
|
||||
|
||||
function collapseAllTreeNodes() {
|
||||
connectionTreeRef.value?.collapseAllTreeNodes();
|
||||
}
|
||||
@@ -178,12 +200,31 @@ defineExpose({ focusSearch, locateTabInSidebar });
|
||||
<div class="app-sidebar-panel h-full shrink-0 relative select-none" :class="classicLayout ? '' : 'rounded-md border border-border/80 bg-background'" :style="{ width: sidebarWidth + 'px' }" @keydown="onSidebarKeydown">
|
||||
<div class="h-full flex flex-col overflow-hidden">
|
||||
<div class="app-sidebar-toolbar flex items-center gap-px px-3 text-xs font-medium text-muted-foreground border-b bg-muted/20" :class="classicLayout ? 'h-9' : 'h-10'">
|
||||
<span class="flex min-w-0 self-stretch items-center" data-tauri-drag-region>
|
||||
<span v-if="showConnectionMultiSelectToolbar" class="flex min-w-0 self-stretch items-center" data-tauri-drag-region>
|
||||
<span class="truncate" data-tauri-drag-region>{{ t("sidebar.connections") }}</span>
|
||||
<span v-if="showConnectionMultiSelectToolbar" class="ml-1.5 shrink-0 text-[11px] font-normal text-muted-foreground/80" data-connection-selection-count>
|
||||
<span class="ml-1.5 shrink-0 text-[11px] font-normal text-muted-foreground/80" data-connection-selection-count>
|
||||
{{ t("connectionGroup.selectedConnections", { count: selectedConnectionCount }) }}
|
||||
</span>
|
||||
</span>
|
||||
<LightDropdown
|
||||
v-else
|
||||
model-value=""
|
||||
:items="connectionTransferItems"
|
||||
:aria-label="connectionTransferLabel"
|
||||
:trigger-title="connectionTransferLabel"
|
||||
:trigger-icon="ArrowDownUp"
|
||||
:trigger-label="connectionTransferLabel"
|
||||
trigger-class="inline-flex h-7 min-w-0 items-center gap-1 rounded-md px-1 outline-none hover:bg-muted hover:text-foreground focus-visible:ring-0"
|
||||
trigger-icon-class="h-3.5 w-3.5 shrink-0"
|
||||
item-icon-class="h-3.5 w-3.5"
|
||||
content-class="w-48"
|
||||
:show-trigger-label="true"
|
||||
:show-chevron="true"
|
||||
:highlight-selected="false"
|
||||
check-position="none"
|
||||
align="start"
|
||||
@update:model-value="selectConnectionTransferAction"
|
||||
/>
|
||||
<span class="flex-1 self-stretch" data-tauri-drag-region />
|
||||
<template v-if="showConnectionMultiSelectToolbar">
|
||||
<LightTooltip :text="t('connectionGroup.createGroup')" side="bottom" :delay="0" :close-delay="0" nowrap>
|
||||
@@ -227,33 +268,9 @@ defineExpose({ focusSearch, locateTabInSidebar });
|
||||
</LightTooltip>
|
||||
</template>
|
||||
<template v-else>
|
||||
<LightTooltip :text="t('sidebar.import')" side="bottom" :delay="0" :close-delay="0" nowrap>
|
||||
<span class="inline-flex">
|
||||
<LightDropdown
|
||||
model-value=""
|
||||
:items="importSourceItems"
|
||||
:aria-label="t('sidebar.import')"
|
||||
:trigger-icon="Download"
|
||||
trigger-class="inline-flex h-6 w-5 items-center justify-center rounded-md outline-none hover:bg-muted hover:text-foreground focus-visible:ring-0"
|
||||
trigger-icon-class="h-4 w-4"
|
||||
content-class="w-44"
|
||||
:show-trigger-label="false"
|
||||
:show-chevron="false"
|
||||
:highlight-selected="false"
|
||||
check-position="none"
|
||||
align="end"
|
||||
@update:model-value="selectImportSource"
|
||||
/>
|
||||
</span>
|
||||
</LightTooltip>
|
||||
<LightTooltip :text="t('sidebar.export')" side="bottom" :delay="0" :close-delay="0" nowrap>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5" @click="emit('export')">
|
||||
<Upload class="h-3 w-3" />
|
||||
</Button>
|
||||
</LightTooltip>
|
||||
<LightTooltip :text="t('sidebar.collapseAll')" side="bottom" :delay="0" :close-delay="0" nowrap>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5" @click="collapseAllTreeNodes">
|
||||
<ChevronsUp class="h-3 w-3" />
|
||||
<ChevronsDownUp class="h-3 w-3" />
|
||||
</Button>
|
||||
</LightTooltip>
|
||||
<LightTooltip :text="t('connectionGroup.createGroup')" side="bottom" :delay="0" :close-delay="0" nowrap>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { ArrowDownWideNarrow, Download, FilePlus, FileText, FolderCog, FolderClo
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import CustomContextMenu, { type ContextMenuItem as CtxMenuItem } from "@/components/ui/CustomContextMenu.vue";
|
||||
import HelpTooltip from "@/components/ui/tooltip/HelpTooltip.vue";
|
||||
import LightTooltip from "@/components/ui/LightTooltip.vue";
|
||||
import { useToast } from "@/composables/useToast";
|
||||
import { isTauriRuntime } from "@/lib/backend/tauriRuntime";
|
||||
@@ -1121,6 +1122,9 @@ function showDropInside(targetId: string) {
|
||||
<div class="h-full flex flex-col overflow-hidden border-l bg-background select-none">
|
||||
<div class="h-9 flex items-center gap-1 px-2 border-b shrink-0 bg-muted/20">
|
||||
<span class="text-[13px] font-medium">{{ t("sqlLibrary.title") }}</span>
|
||||
<HelpTooltip :label="t('sqlLibrary.storageHelp')" side="bottom" :side-offset="4" trigger-class="h-4 w-4" content-class="max-w-[320px] whitespace-pre-line">
|
||||
{{ t("sqlLibrary.storageHelp") }}
|
||||
</HelpTooltip>
|
||||
<span v-if="hasSelection" class="text-[12px] text-muted-foreground ml-1">({{ selectedCount }})</span>
|
||||
<span class="flex-1" />
|
||||
<LightTooltip :text="sortMode === 'folder' ? t('sqlLibrary.sortByDate') : t('sqlLibrary.sortByFolder')" side="bottom" :delay="0" :close-delay="0" nowrap>
|
||||
|
||||
@@ -4,6 +4,12 @@ import { describe, expect, it } from "vitest";
|
||||
const panelSource = readFileSync(new URL("../SqlLibraryPanel.vue", import.meta.url), "utf8");
|
||||
|
||||
describe("SqlLibraryPanel selection contrast", () => {
|
||||
it("explains how SQL Library content differs from local SQL files", () => {
|
||||
expect(panelSource).toContain('import HelpTooltip from "@/components/ui/tooltip/HelpTooltip.vue";');
|
||||
expect(panelSource).toMatch(/<HelpTooltip :label="t\('sqlLibrary\.storageHelp'\)" side="bottom"[\s\S]*?\{\{ t\("sqlLibrary\.storageHelp"\) \}\}[\s\S]*?<\/HelpTooltip>/);
|
||||
expect(panelSource).toContain('content-class="max-w-[320px] whitespace-pre-line"');
|
||||
});
|
||||
|
||||
it("uses the accent foreground for selected rows and their metadata", () => {
|
||||
expect(panelSource).toContain('return "bg-accent text-accent-foreground";');
|
||||
expect(panelSource).toMatch(/function fileMetaClass[\s\S]*\? "text-accent-foreground" : "text-muted-foreground";/);
|
||||
|
||||
Reference in new issue
Block a user