fix(editor): let mouse hide/re-show the shared query results pane
This commit is contained in:
1 parent
b8d4388caf
commit
73c167bed3
5 files changed
+85
-10
No files matched your search
@@ -664,6 +664,21 @@ function toggleResultsPane(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The "hide results" chevron. In the shared result surface (result-only) the
|
||||
* local resultsPaneOpen ref is inert — the pane is always rendered there and
|
||||
* the real collapse state lives in SqlEditorWorkspace.showResultPane — so the
|
||||
* chevron bubbles a toggle event up instead. Per-tab surfaces (data tabs)
|
||||
* keep the local collapse behavior.
|
||||
*/
|
||||
function handleHideResultsPane() {
|
||||
if (props.resultOnly) {
|
||||
emit("toggleResultsPane");
|
||||
} else {
|
||||
resultsPaneOpen.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onResultsResized(payload: { panes: { size: number }[] }) {
|
||||
const resultsPane = payload.panes[1];
|
||||
if (resultsPane?.size != null && resultsPane.size >= 20 && resultsPane.size <= 85) {
|
||||
@@ -1664,7 +1679,7 @@ defineExpose({
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<LightTooltip :text="t('editor.hideResultsPane')" side="bottom" :delay="0" :close-delay="0" nowrap>
|
||||
<Button variant="ghost" size="icon" class="h-6 w-7 shrink-0 text-muted-foreground hover:text-foreground" :title="t('editor.hideResultsPane')" :aria-label="t('editor.hideResultsPane')" @click="resultsPaneOpen = false">
|
||||
<Button variant="ghost" size="icon" class="h-6 w-7 shrink-0 text-muted-foreground hover:text-foreground" :title="t('editor.hideResultsPane')" :aria-label="t('editor.hideResultsPane')" @click="handleHideResultsPane">
|
||||
<ChevronDown class="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</LightTooltip>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onMounted, ref, useAttrs } from "vue";
|
||||
import { computed, nextTick, onMounted, ref, useAttrs, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Splitpanes, Pane } from "splitpanes";
|
||||
import "splitpanes/dist/splitpanes.css";
|
||||
import "./sqlEditorWorkspace.css";
|
||||
import { useQueryStore } from "@/stores/queryStore";
|
||||
import { safeLocalStorageGet, safeLocalStorageSet } from "@/lib/backend/safeStorage";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import EditorGroup from "./EditorGroup.vue";
|
||||
import QueryResultSurface from "./QueryResultSurface.vue";
|
||||
import { createContentSurfaceEventForwarders } from "@/lib/tabs/contentSurfaceEvents";
|
||||
@@ -71,13 +73,7 @@ defineExpose({
|
||||
const group = groupForElement(element) ?? activeEditorGroup();
|
||||
return group?.refreshData() ?? resultSurfaceRef.value?.refreshData() ?? false;
|
||||
},
|
||||
toggleResultsPane: () => {
|
||||
if (!showSharedResult.value) {
|
||||
return activeEditorGroup()?.toggleResultsPane() ?? false;
|
||||
}
|
||||
showResultPane.value = !showResultPane.value;
|
||||
return true;
|
||||
},
|
||||
toggleResultsPane: () => toggleSharedResultsPane(),
|
||||
refreshQueryEditorCompletionCache: () => activeEditorGroup()?.refreshQueryEditorCompletionCache() ?? false,
|
||||
handleModRTarget: (target: Element) => {
|
||||
// DataGrid, Elasticsearch JSON, and the cell-detail editor (Teleported
|
||||
@@ -107,6 +103,7 @@ defineExpose({
|
||||
executeRedisCommand: (command: string) => (groupForElement(commandTargetElement(null)) ?? activeEditorGroup())?.executeRedisCommand(command) ?? Promise.resolve(false),
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const queryStore = useQueryStore();
|
||||
const activeTab = computed(() => queryStore.tabs.find((tab) => tab.id === queryStore.activeTabId));
|
||||
const showSharedResult = computed(() => activeTab.value?.mode === "query");
|
||||
@@ -124,6 +121,31 @@ const showResultPane = ref(true);
|
||||
// (globals.css kills it for horizontal splitpanes).
|
||||
const resultPaneTargetSize = computed(() => (showSharedResult.value && showResultPane.value ? resultPaneSize.value : 0));
|
||||
const editorPaneSize = computed(() => 100 - resultPaneTargetSize.value);
|
||||
|
||||
/**
|
||||
* Single toggle entry point for the shared result pane. Reached from the
|
||||
* keyboard shortcut (exposed toggleResultsPane) and from the shared surface's
|
||||
* "hide results" chevron, which bubbles a toggleResultsPane event up through
|
||||
* ContentArea → QueryResultSurface. Non-query tabs (data etc.) render a plain
|
||||
* ContentArea and keep their own per-tab resultsPaneOpen collapse behavior.
|
||||
*/
|
||||
function toggleSharedResultsPane(): boolean {
|
||||
if (!showSharedResult.value) {
|
||||
return activeEditorGroup()?.toggleResultsPane() ?? false;
|
||||
}
|
||||
showResultPane.value = !showResultPane.value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Restores the pre-split "running a query re-expands the results pane"
|
||||
// behavior (see issue #6193): a collapsed shared pane comes back when the
|
||||
// active tab starts executing.
|
||||
watch(
|
||||
() => activeTab.value?.isExecuting,
|
||||
(isExecuting) => {
|
||||
if (isExecuting) showResultPane.value = true;
|
||||
},
|
||||
);
|
||||
// Entrance choreography is hydration-gated: groups present at first render
|
||||
// never animate (no load choreography); only groups created later — by a
|
||||
// split — materialize from their owner's side of the divider.
|
||||
@@ -195,7 +217,7 @@ function handleFocusStatement(tabId: string, range: StatementRange | null): bool
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sql-editor-workspace flex h-full min-h-0 flex-1 flex-col overflow-hidden" :class="workspaceClass">
|
||||
<div class="sql-editor-workspace relative flex h-full min-h-0 flex-1 flex-col overflow-hidden" :class="workspaceClass">
|
||||
<Splitpanes horizontal class="sql-editor-workspace-split flex-1 min-h-0" :class="{ 'result-pane-collapsed': resultPaneTargetSize === 0 }" @resized="onSharedResultResized">
|
||||
<Pane class="min-h-0 min-w-0" :size="editorPaneSize" :min-size="100 - SHARED_RESULT_PANE_MAX_SIZE">
|
||||
<Splitpanes
|
||||
@@ -237,6 +259,7 @@ function handleFocusStatement(tabId: string, range: StatementRange | null): bool
|
||||
ref="resultSurfaceRef"
|
||||
v-bind="resultSurfaceBindings"
|
||||
class="h-full"
|
||||
@toggle-results-pane="toggleSharedResultsPane"
|
||||
@preview-statement="
|
||||
(tabId: string, range: { from: number; to: number } | null) => {
|
||||
handlePreviewStatement(tabId, range);
|
||||
@@ -252,5 +275,19 @@ function handleFocusStatement(tabId: string, range: StatementRange | null): bool
|
||||
</Transition>
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
<!-- The shared surface unmounts when collapsed, so the mouse re-show
|
||||
affordance lives here, outside the collapsible pane. -->
|
||||
<Button
|
||||
v-if="showSharedResult && !showResultPane"
|
||||
type="button"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
class="absolute bottom-3 right-3 z-20 h-7 gap-1.5 rounded-full border bg-background/95 px-3 text-xs shadow-lg hover:bg-accent"
|
||||
:title="t('editor.showResultsPane')"
|
||||
:aria-label="t('editor.showResultsPane')"
|
||||
@click="showResultPane = true"
|
||||
>
|
||||
{{ t("editor.showResultsPane") }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -9,6 +9,7 @@ const editorSurfaceSource = readFileSync(new URL("../QueryEditorSurface.vue", im
|
||||
const resultSurfaceSource = readFileSync(new URL("../QueryResultSurface.vue", import.meta.url), "utf8");
|
||||
const contentAreaSource = readFileSync(new URL("../ContentArea.vue", import.meta.url), "utf8");
|
||||
const surfaceContractSource = readFileSync(new URL("../querySurfaces.ts", import.meta.url), "utf8");
|
||||
const surfaceEventsSource = readFileSync(new URL("../../../lib/tabs/contentSurfaceEvents.ts", import.meta.url), "utf8");
|
||||
|
||||
describe("SQL editor workspace single-group tracer bullet", () => {
|
||||
it("composes splitpanes editor groups and one shared result surface", () => {
|
||||
@@ -81,6 +82,26 @@ describe("SQL editor workspace single-group tracer bullet", () => {
|
||||
expect(resultSurfaceSource).toContain('v-bind="bindings"');
|
||||
});
|
||||
|
||||
it("lets the mouse hide and re-show the shared query results pane", () => {
|
||||
// Regression (#8076 refactor): the shared result surface's "hide results"
|
||||
// chevron wrote the inert per-instance resultsPaneOpen ref, so query-tab
|
||||
// results could no longer be collapsed by mouse — the pane was pinned
|
||||
// open. The chevron must bubble a toggleResultsPane event up through the
|
||||
// surface contract to the workspace, which owns the real collapse state.
|
||||
expect(surfaceContractSource).toContain("toggleResultsPane: []");
|
||||
expect(surfaceEventsSource).toContain('"toggleResultsPane",');
|
||||
expect(contentAreaSource).toContain('emit("toggleResultsPane")');
|
||||
expect(contentAreaSource).toContain('@click="handleHideResultsPane"');
|
||||
expect(workspaceSource).toContain('@toggle-results-pane="toggleSharedResultsPane"');
|
||||
expect(workspaceSource).toContain("showResultPane.value = !showResultPane.value");
|
||||
// Collapsing unmounts the shared surface, so the mouse re-show affordance
|
||||
// must live at the workspace level, not inside the collapsible pane.
|
||||
expect(workspaceSource).toContain("editor.showResultsPane");
|
||||
expect(workspaceSource).toContain("showSharedResult && !showResultPane");
|
||||
// Running a query re-expands a collapsed pane (issue #6193 promise).
|
||||
expect(workspaceSource).toContain("if (isExecuting) showResultPane.value = true;");
|
||||
});
|
||||
|
||||
it("animates layout transitions for split groups and the shared result surface", () => {
|
||||
const workspaceCssSource = readFileSync(new URL("../sqlEditorWorkspace.css", import.meta.url), "utf8");
|
||||
expect(workspaceSource).toContain('import "./sqlEditorWorkspace.css"');
|
||||
|
||||
@@ -95,4 +95,5 @@ export interface ContentAreaSurfaceEmits {
|
||||
openSettings: [initialTab?: string, initialSection?: string];
|
||||
openConnectionSettings: [connectionId: string, initialTab: "advanced"];
|
||||
toggleZenMode: [];
|
||||
toggleResultsPane: [];
|
||||
}
|
||||
Reference in new issue
Block a user