fix(ui): unify special tab activation styles
This commit is contained in:
5 files changed
+62
-19
No files matched your search
@@ -28,7 +28,6 @@ const props = defineProps<{
|
||||
const emit = defineEmits<{
|
||||
"activate-driver-store": [];
|
||||
"close-driver-store": [];
|
||||
"activate-plugin-center": [];
|
||||
"close-plugin-center": [];
|
||||
"activate-settings-page": [];
|
||||
"close-settings-page": [];
|
||||
@@ -202,9 +201,6 @@ function handleCancelClose() {
|
||||
<div v-show="driverStoreActive || pluginCenterActive || settingsPageActive" data-special-page-workspace class="flex min-h-0 min-w-0 flex-1 overflow-hidden" :class="layoutClass">
|
||||
<div data-special-page-navigation class="flex min-h-0 min-w-0 shrink-0 flex-col overflow-auto" :style="navigationStyle">
|
||||
<div v-for="group in queryStore.groups" :key="group.id" :ref="(element) => setTabBarTarget(group.id, element)" :data-special-page-tab-target="group.id" class="flex min-h-0 min-w-0" :class="isVerticalLayout ? 'flex-1' : 'shrink-0'" />
|
||||
<button v-if="pluginCenterOpen" type="button" data-plugin-center-tab class="shrink-0 rounded-md px-2 py-1 text-left text-sm" :class="pluginCenterActive ? 'bg-accent text-accent-foreground' : 'text-muted-foreground'" @click="emit('activate-plugin-center')">
|
||||
{{ t("toolbar.pluginCenter") }}
|
||||
</button>
|
||||
</div>
|
||||
<div data-special-page-content class="flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden">
|
||||
<slot />
|
||||
|
||||
@@ -159,6 +159,8 @@ const groupExecutableSql = computed(() => {
|
||||
@close-settings="toolbar.closeSettingsPage()"
|
||||
@activate-driver-store="toolbar.activateDriverStore()"
|
||||
@close-driver-store="toolbar.closeDriverStore()"
|
||||
@activate-plugin-center="toolbar.activatePluginCenter()"
|
||||
@close-plugin-center="toolbar.closePluginCenter()"
|
||||
/>
|
||||
</Teleport>
|
||||
<!-- The toolbar stays at the top of the pane's content column in every
|
||||
|
||||
@@ -53,6 +53,7 @@ import {
|
||||
PanelTop,
|
||||
Pencil,
|
||||
Pin,
|
||||
PlugZap,
|
||||
RotateCcw,
|
||||
RotateCw,
|
||||
Search,
|
||||
@@ -98,8 +99,8 @@ const props = defineProps<{
|
||||
canDetachTabs?: boolean;
|
||||
/** A detached tab is being dragged over this bar — highlight it as the drop target. */
|
||||
detachedDropTarget?: boolean;
|
||||
/** App-level special pages (settings / driver store) appended after the tabs. */
|
||||
specialPageTabs?: { settingsOpen: boolean; settingsActive: boolean; driverStoreOpen: boolean; driverStoreActive: boolean; driverUpdateCount: number };
|
||||
/** App-level special pages appended after the tabs. */
|
||||
specialPageTabs?: { settingsOpen: boolean; settingsActive: boolean; driverStoreOpen: boolean; driverStoreActive: boolean; pluginCenterOpen: boolean; pluginCenterActive: boolean; driverUpdateCount: number };
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -113,6 +114,8 @@ const emit = defineEmits<{
|
||||
"close-settings": [];
|
||||
"activate-driver-store": [];
|
||||
"close-driver-store": [];
|
||||
"activate-plugin-center": [];
|
||||
"close-plugin-center": [];
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
@@ -135,8 +138,8 @@ const suppressNextTabClick = ref(false);
|
||||
const isClassicLayout = computed(() => settingsStore.editorSettings.appLayout === "classic");
|
||||
// Special pages append to the focused group's strip only: one instance at a
|
||||
// time, in the pane the user is working in (v0.6.2 kept them in the single strip).
|
||||
const showSpecialPageTabs = computed(() => !!props.specialPageTabs && (props.specialPageTabs.settingsOpen || props.specialPageTabs.driverStoreOpen) && queryStore.focusedGroupId === props.groupId);
|
||||
const specialPageActive = computed(() => !!(props.specialPageTabs?.settingsActive || props.specialPageTabs?.driverStoreActive));
|
||||
const showSpecialPageTabs = computed(() => !!props.specialPageTabs && (props.specialPageTabs.settingsOpen || props.specialPageTabs.driverStoreOpen || props.specialPageTabs.pluginCenterOpen) && queryStore.focusedGroupId === props.groupId);
|
||||
const specialPageActive = computed(() => !!(props.specialPageTabs?.settingsActive || props.specialPageTabs?.driverStoreActive || props.specialPageTabs?.pluginCenterActive));
|
||||
|
||||
function isTabActive(tab: QueryTab): boolean {
|
||||
return !specialPageActive.value && tab.id === props.activeTabId;
|
||||
@@ -153,9 +156,11 @@ function specialPageTabClass(active: boolean): string[] {
|
||||
}
|
||||
|
||||
function specialPageTabStyle(active: boolean) {
|
||||
if (isVerticalLayout.value) return active ? { "--app-tab-background": "var(--accent)" } : undefined;
|
||||
if (!isClassicLayout.value) return undefined;
|
||||
return active ? { boxShadow: "inset 0 -2px 0 var(--ring)" } : undefined;
|
||||
if (!active) return undefined;
|
||||
const activeBackground = "color-mix(in srgb, var(--foreground) 18%, var(--background))";
|
||||
if (isVerticalLayout.value) return { "--app-tab-background": "var(--accent)" };
|
||||
if (!isClassicLayout.value) return { "--app-tab-background": activeBackground, borderColor: "var(--ring)" };
|
||||
return { "--app-tab-background": activeBackground, boxShadow: "inset 0 -2px 0 color-mix(in srgb, var(--foreground) 72%, transparent)" };
|
||||
}
|
||||
const isVerticalLayout = computed(() => settingsStore.editorSettings.tabPlacement === "left" || settingsStore.editorSettings.tabPlacement === "right");
|
||||
const isWrapLayout = computed(() => !isVerticalLayout.value && settingsStore.editorSettings.tabLayout === "wrap");
|
||||
@@ -192,16 +197,22 @@ function toggleCompactTabTitle() {
|
||||
compactTabTitle.value = !compactTabTitle.value;
|
||||
}
|
||||
|
||||
function getSpecialPageTabMenuItems(surface: "settings" | "driverStore"): ContextMenuItem[] {
|
||||
const closeCurrent = surface === "settings" ? () => emit("close-settings") : () => emit("close-driver-store");
|
||||
const otherOpen = surface === "settings" ? props.specialPageTabs?.driverStoreOpen : props.specialPageTabs?.settingsOpen;
|
||||
function getSpecialPageTabMenuItems(surface: "settings" | "driverStore" | "pluginCenter"): ContextMenuItem[] {
|
||||
const closeCurrent = surface === "settings" ? () => emit("close-settings") : surface === "driverStore" ? () => emit("close-driver-store") : () => emit("close-plugin-center");
|
||||
const otherOpen =
|
||||
surface === "settings" ? props.specialPageTabs?.driverStoreOpen || props.specialPageTabs?.pluginCenterOpen : surface === "driverStore" ? props.specialPageTabs?.settingsOpen || props.specialPageTabs?.pluginCenterOpen : props.specialPageTabs?.settingsOpen || props.specialPageTabs?.driverStoreOpen;
|
||||
const closeOthers = () => {
|
||||
if (surface !== "settings") emit("close-settings");
|
||||
if (surface !== "driverStore") emit("close-driver-store");
|
||||
if (surface !== "pluginCenter") emit("close-plugin-center");
|
||||
};
|
||||
return [
|
||||
{ label: compactTabTitle.value ? t("contextMenu.fullTabTitle") : t("contextMenu.compactTabTitle"), action: toggleCompactTabTitle, icon: compactTabTitle.value ? Maximize2 : Minimize2 },
|
||||
{ label: "", separator: true },
|
||||
{ label: t("contextMenu.closeTab"), action: closeCurrent, icon: X },
|
||||
{
|
||||
label: t("contextMenu.closeOtherTabs"),
|
||||
action: () => (surface === "settings" ? emit("close-driver-store") : emit("close-settings")),
|
||||
action: closeOthers,
|
||||
disabled: !otherOpen,
|
||||
icon: X,
|
||||
},
|
||||
@@ -1392,7 +1403,7 @@ watch(
|
||||
{ flush: "post" },
|
||||
);
|
||||
|
||||
watch([() => props.specialPageTabs?.settingsActive, () => props.specialPageTabs?.driverStoreActive, () => settingsStore.editorSettings.tabPlacement, () => props.tabBarCollapsed], () => {
|
||||
watch([() => props.specialPageTabs?.settingsActive, () => props.specialPageTabs?.driverStoreActive, () => props.specialPageTabs?.pluginCenterActive, () => settingsStore.editorSettings.tabPlacement, () => props.tabBarCollapsed], () => {
|
||||
nextTick(() => {
|
||||
updateScrollButtons();
|
||||
if (showSpecialPageTabs.value && specialPageActive.value) {
|
||||
@@ -1614,6 +1625,31 @@ watch([() => props.specialPageTabs?.settingsActive, () => props.specialPageTabs?
|
||||
</CustomContextMenu>
|
||||
</template>
|
||||
<template v-if="!section.pinned && showSpecialPageTabs">
|
||||
<CustomContextMenu v-if="specialPageTabs?.pluginCenterOpen" :items="getSpecialPageTabMenuItems('pluginCenter')" v-slot="{ onContextMenu }">
|
||||
<div
|
||||
data-plugin-center-tab
|
||||
class="app-tab-pill group flex shrink-0 cursor-default items-center gap-1 px-2 text-xs transition-colors whitespace-nowrap select-none"
|
||||
:class="specialPageTabClass(!!specialPageTabs?.pluginCenterActive)"
|
||||
:style="specialPageTabStyle(!!specialPageTabs?.pluginCenterActive)"
|
||||
:data-active-tab="specialPageTabs?.pluginCenterActive"
|
||||
:title="t('toolbar.pluginCenter')"
|
||||
:aria-label="t('toolbar.pluginCenter')"
|
||||
:aria-pressed="!!specialPageTabs?.pluginCenterActive"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="emit('activate-plugin-center')"
|
||||
@keydown.enter.self.prevent="emit('activate-plugin-center')"
|
||||
@keydown.space.self.prevent="emit('activate-plugin-center')"
|
||||
@contextmenu="onContextMenu"
|
||||
@mousedown.middle.prevent="emit('close-plugin-center')"
|
||||
>
|
||||
<PlugZap class="h-3.5 w-3.5 shrink-0 text-violet-600 dark:text-violet-400" />
|
||||
<span v-if="!isTabBarCollapsed" class="min-w-0 flex-1 truncate">{{ t("toolbar.pluginCenter") }}</span>
|
||||
<button v-if="!isTabBarCollapsed" class="shrink-0 rounded p-0.5 hover:bg-muted-foreground/20" :aria-label="t('common.close')" :title="t('common.close')" @click.stop="emit('close-plugin-center')">
|
||||
<X class="h-3 w-3" />
|
||||
</button>
|
||||
</div>
|
||||
</CustomContextMenu>
|
||||
<CustomContextMenu v-if="specialPageTabs?.settingsOpen" :items="getSpecialPageTabMenuItems('settings')" v-slot="{ onContextMenu }">
|
||||
<div
|
||||
data-settings-page-tab
|
||||
|
||||
@@ -9,12 +9,14 @@ import type { InjectionKey, Ref } from "vue";
|
||||
* the acting tab from the explicit tabId argument (never re-reading the global
|
||||
* active tab after an await).
|
||||
*/
|
||||
/** Open/active state of the App-level special pages (settings / driver store). */
|
||||
/** Open/active state of the App-level special pages. */
|
||||
export interface SpecialPageTabsState {
|
||||
settingsOpen: boolean;
|
||||
settingsActive: boolean;
|
||||
driverStoreOpen: boolean;
|
||||
driverStoreActive: boolean;
|
||||
pluginCenterOpen: boolean;
|
||||
pluginCenterActive: boolean;
|
||||
driverUpdateCount: number;
|
||||
}
|
||||
|
||||
@@ -49,6 +51,8 @@ export interface EditorToolbarActions {
|
||||
closeSettingsPage(): void;
|
||||
activateDriverStore(): void;
|
||||
closeDriverStore(): void;
|
||||
activatePluginCenter(): void;
|
||||
closePluginCenter(): void;
|
||||
}
|
||||
|
||||
export const EDITOR_TOOLBAR_ACTIONS: InjectionKey<EditorToolbarActions> = Symbol("dbx:editor-toolbar-actions");
|
||||
@@ -63,7 +67,7 @@ export function createNoopEditorToolbarActions(): EditorToolbarActions {
|
||||
const mode = { value: "explain" } as Ref<"explain" | "autotrace">;
|
||||
const flag = { value: true } as Ref<boolean>;
|
||||
const specialPageTabs = {
|
||||
value: { settingsOpen: false, settingsActive: false, driverStoreOpen: false, driverStoreActive: false, driverUpdateCount: 0 },
|
||||
value: { settingsOpen: false, settingsActive: false, driverStoreOpen: false, driverStoreActive: false, pluginCenterOpen: false, pluginCenterActive: false, driverUpdateCount: 0 },
|
||||
} as Ref<SpecialPageTabsState>;
|
||||
return {
|
||||
explainMode: mode,
|
||||
@@ -93,5 +97,7 @@ export function createNoopEditorToolbarActions(): EditorToolbarActions {
|
||||
closeSettingsPage: noop,
|
||||
activateDriverStore: noop,
|
||||
closeDriverStore: noop,
|
||||
activatePluginCenter: noop,
|
||||
closePluginCenter: noop,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user