feat: reorganize toolbar with more dropdown and add theme to appearance settings

This commit is contained in:
t8y2 2026-06-11 13:13:23 +08:00
parent 26d714909e
commit 4a264d638c
2 changed files with 61 additions and 62 deletions

View File

@ -2,7 +2,7 @@
import { ref, watch, shallowRef, computed, onMounted } from "vue";
import type { EditorView as EditorViewType } from "@codemirror/view";
import { useI18n } from "vue-i18n";
import { AlertTriangle, CheckCircle2, CircleHelp, Cloud, Copy, Download, ExternalLink, Loader2, PackageSearch, Pencil, RefreshCw, RotateCcw, Settings, Terminal, Trash2, Upload, X } from "@lucide/vue";
import { AlertTriangle, CheckCircle2, CircleHelp, Cloud, Copy, Download, ExternalLink, Loader2, Moon, PackageSearch, Pencil, RefreshCw, RotateCcw, Settings, Sun, SunMoon, Terminal, Trash2, Upload, X } from "@lucide/vue";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
@ -56,7 +56,7 @@ import { LOCALE_OPTIONS } from "@/lib/localeOptions";
const { t } = useI18n();
const settingsStore = useSettingsStore();
const connectionStore = useConnectionStore();
const { isDark } = useTheme();
const { isDark, themeMode, setThemeMode } = useTheme();
const props = defineProps<{
open: boolean;
@ -1433,6 +1433,29 @@ watch(
</Select>
</div>
<div class="space-y-2">
<Label>{{ t("settings.theme") }}</Label>
<div class="flex gap-2">
<Button
v-for="option in [
{ value: 'light', label: t('toolbar.themeLight'), icon: Sun },
{ value: 'dark', label: t('toolbar.themeDark'), icon: Moon },
{ value: 'system', label: t('toolbar.themeSystem'), icon: SunMoon },
]"
:key="option.value"
type="button"
variant="outline"
size="sm"
class="h-auto gap-1.5 px-3 py-1.5"
:class="themeMode === option.value ? 'border-blue-300 ring-2 ring-blue-300/50' : ''"
@click="setThemeMode(option.value as 'light' | 'dark' | 'system')"
>
<component :is="option.icon" class="h-3.5 w-3.5" />
{{ option.label }}
</Button>
</div>
</div>
<Separator />
<div class="space-y-2">

View File

@ -1,16 +1,14 @@
<script setup lang="ts">
import { computed, ref, onMounted, onBeforeUnmount } from "vue";
import { useI18n } from "vue-i18n";
import { DatabaseZap, FilePlus2, Loader2, Languages, Moon, Sun, SunMoon, History, Bot, ArrowLeftRight, FileCode, FileStack, GitCompareArrows, TableProperties, Settings, CloudDownload, Package, Ellipsis } from "@lucide/vue";
import { DatabaseZap, FilePlus2, Loader2, Moon, Sun, SunMoon, History, Bot, ArrowLeftRight, FileCode, BookMarked, GitCompareArrows, TableProperties, Settings, CloudDownload, Package } from "@lucide/vue";
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import LightDropdown from "@/components/ui/LightDropdown.vue";
import WindowControls from "@/components/layout/WindowControls.vue";
import ExportProgressPopover from "@/components/export/ExportProgressPopover.vue";
import { shouldReserveMacTrafficLightInset, useWindowControls } from "@/composables/useWindowControls";
import { currentLocale, setLocale, type Locale } from "@/i18n";
import type { AppThemeMode } from "@/lib/appTheme";
import { LOCALE_OPTIONS } from "@/lib/localeOptions";
const props = defineProps<{
isDark: boolean;
@ -51,13 +49,6 @@ const themeItems = computed(() => [
{ value: "dark", label: t("toolbar.themeDark"), icon: Moon },
{ value: "system", label: t("toolbar.themeSystem"), icon: SunMoon },
]);
const localeItems = computed(() =>
LOCALE_OPTIONS.map((option) => ({
value: option.value,
label: option.label,
leadingText: option.flag,
})),
);
const themeTriggerIcon = computed(() => {
if (props.themeMode === "system") return SunMoon;
return props.isDark ? Moon : Sun;
@ -91,14 +82,7 @@ onBeforeUnmount(() => {
resizeObserver?.disconnect();
});
const collapsedItems = computed(() => [
{
value: "transfer",
label: t("transfer.dataTransfer"),
icon: ArrowLeftRight,
action: () => emit("open-transfer"),
disabled: !props.hasConnections,
},
const moreItems = computed(() => [
{
value: "sql-file",
label: t("sqlFile.title"),
@ -120,6 +104,17 @@ const collapsedItems = computed(() => [
action: () => emit("open-data-compare"),
disabled: !props.hasConnections,
},
]);
const collapsedItems = computed(() => [
{
value: "transfer",
label: t("transfer.dataTransfer"),
icon: ArrowLeftRight,
action: () => emit("open-transfer"),
disabled: !props.hasConnections,
},
...moreItems.value,
{
value: "driver-store",
label: props.agentDriverUpdateCount > 0 ? `${t("toolbar.driverManager")} (${props.agentDriverUpdateCount})` : t("toolbar.driverManager"),
@ -148,21 +143,6 @@ const collapsedItems = computed(() => [
{{ t("transfer.dataTransfer") }}
</Button>
<Button variant="ghost" size="sm" class="h-8 px-2 text-xs gap-1" @click="emit('open-sql-file')" :disabled="!hasSqlFileConnections">
<FileCode class="h-3.5 w-3.5" />
{{ t("sqlFile.title") }}
</Button>
<Button variant="ghost" size="sm" class="h-8 px-2 text-xs gap-1" @click="emit('open-schema-diff')" :disabled="!hasConnections">
<GitCompareArrows class="h-3.5 w-3.5" />
{{ t("diff.title") }}
</Button>
<Button variant="ghost" size="sm" class="h-8 px-2 text-xs gap-1" @click="emit('open-data-compare')" :disabled="!hasConnections">
<TableProperties class="h-3.5 w-3.5" />
{{ t("dataCompare.title") }}
</Button>
<Button variant="ghost" size="sm" class="h-8 px-2 text-xs gap-1" :class="{ 'bg-accent': showDriverStore }" @click="emit('open-driver-store')">
<Package class="h-3.5 w-3.5" />
{{ t("toolbar.driverManager") }}
@ -170,6 +150,24 @@ const collapsedItems = computed(() => [
{{ agentDriverUpdateCount > 99 ? "99+" : agentDriverUpdateCount }}
</span>
</Button>
<LightDropdown
model-value=""
:items="moreItems"
:aria-label="t('common.more')"
:trigger-label="t('common.more')"
trigger-class="inline-flex h-8 items-center gap-1 rounded-lg px-2 text-xs font-medium hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 transition-colors"
:show-trigger-label="true"
:show-chevron="true"
check-position="none"
align="start"
@update:model-value="
(value) => {
const item = moreItems.find((i) => i.value === value);
item?.action();
}
"
/>
</template>
<template v-if="toolbarCollapsed">
@ -177,11 +175,10 @@ const collapsedItems = computed(() => [
model-value=""
:items="collapsedItems"
:aria-label="t('common.more')"
:trigger-icon="Ellipsis"
trigger-class="inline-flex h-8 w-8 items-center justify-center rounded-md hover:bg-accent hover:text-accent-foreground"
trigger-icon-class="h-4 w-4"
:show-trigger-label="false"
:show-chevron="false"
:trigger-label="t('common.more')"
trigger-class="inline-flex h-8 items-center gap-1 rounded-lg px-2 text-xs font-medium hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 transition-colors"
:show-trigger-label="true"
:show-chevron="true"
check-position="none"
align="start"
@update:model-value="
@ -211,7 +208,7 @@ const collapsedItems = computed(() => [
<Tooltip>
<TooltipTrigger as-child>
<Button variant="ghost" size="icon" class="h-8 w-8" :class="{ 'bg-accent': showSqlLibrary }" @click="emit('toggle-sql-library')">
<FileStack class="h-4 w-4" />
<BookMarked class="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>{{ t("sqlLibrary.title") }}</TooltipContent>
@ -257,27 +254,6 @@ const collapsedItems = computed(() => [
<TooltipContent>{{ t("toolbar.theme") }}</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger as-child>
<span class="inline-flex">
<LightDropdown
:model-value="currentLocale()"
:items="localeItems"
:aria-label="t('common.language')"
:trigger-icon="Languages"
trigger-class="inline-flex h-8 w-8 items-center justify-center rounded-md hover:bg-accent hover:text-accent-foreground"
trigger-icon-class="h-4 w-4"
:show-trigger-label="false"
:show-chevron="false"
check-position="none"
align="end"
@update:model-value="(value) => setLocale(value as Locale)"
/>
</span>
</TooltipTrigger>
<TooltipContent>{{ t("common.language") }}</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger as-child>
<Button variant="ghost" size="icon" class="h-8 w-8" @click="emit('open-github')">