fix(grid): make data toolbar compact mode responsive
This commit is contained in:
5 files changed
+69
-17
No files matched your search
@@ -207,7 +207,7 @@ import { useConnectionStore } from "@/stores/connectionStore";
|
||||
import { useQueryStore } from "@/stores/queryStore";
|
||||
import { useSettingsStore } from "@/stores/settingsStore";
|
||||
import { simpleDataGridOrderByMatchesSort, simpleDataGridOrderByReferencesMissingColumn, type DataGridSortDirection, type DataGridSortMode } from "@/lib/dataGrid/dataGridSort";
|
||||
import { DATA_GRID_COMPACT_TOPBAR_WIDTH, type DataGridReloadIntent, type DataGridToolbarActionCapability, type DataGridToolbarAutoRefreshCapability, type DataGridToolbarSaveCapability } from "@/lib/dataGrid/dataGridToolbar";
|
||||
import { DATA_GRID_CONDITION_TOOLBAR_MIN_WIDTH, isDataGridToolbarCompact, type DataGridReloadIntent, type DataGridToolbarActionCapability, type DataGridToolbarAutoRefreshCapability, type DataGridToolbarSaveCapability } from "@/lib/dataGrid/dataGridToolbar";
|
||||
import { getTableMetadataCapabilities } from "@/lib/table/tableMetadataCapabilities";
|
||||
import { getTableStructureCapabilities } from "@/lib/table/tableStructureCapabilities";
|
||||
import { filterObjectBrowserTableColumns } from "@/lib/table/objectBrowserTableInfo";
|
||||
@@ -458,12 +458,13 @@ const columnCommentMap = computed(() => {
|
||||
return map;
|
||||
});
|
||||
const dataGridTopbarWidth = ref(0);
|
||||
const dataGridViewportWidth = ref(0);
|
||||
const showColumnCommentsInHeader = computed(() => settingsStore.editorSettings.showColumnCommentsInHeader);
|
||||
const showColumnTypesInHeader = computed(() => settingsStore.editorSettings.showColumnTypesInHeader);
|
||||
const compactColumnHeaderActions = computed(() => settingsStore.editorSettings.compactColumnHeaderActions);
|
||||
const dataGridRenderMode = computed(() => settingsStore.editorSettings.dataGridRenderMode);
|
||||
const dataGridSearchMode = computed(() => settingsStore.editorSettings.dataGridSearchMode);
|
||||
const compactDataGridToolbar = computed(() => dataGridTopbarWidth.value > 0 && dataGridTopbarWidth.value < DATA_GRID_COMPACT_TOPBAR_WIDTH);
|
||||
const compactDataGridToolbar = computed(() => isDataGridToolbarCompact(dataGridTopbarWidth.value, dataGridViewportWidth.value, DATA_GRID_CONDITION_TOOLBAR_MIN_WIDTH));
|
||||
const infiniteScrollEnabled = computed(() => props.paginationEnabled && settingsStore.editorSettings.infiniteScroll);
|
||||
const infiniteScrollMaxRows = computed(() => settingsStore.editorSettings.infiniteScrollMaxRows);
|
||||
const expandedCellEditor = ref<{ rowId: number; col: number } | null>(null);
|
||||
@@ -2068,6 +2069,7 @@ function observeGridHorizontalScrollbarScroller() {
|
||||
|
||||
function updateDataGridTopbarWidth() {
|
||||
dataGridTopbarWidth.value = dataGridTopbarRef.value?.clientWidth ?? 0;
|
||||
dataGridViewportWidth.value = typeof window === "undefined" ? 0 : window.innerWidth;
|
||||
}
|
||||
|
||||
function observeDataGridTopbarWidth() {
|
||||
@@ -4603,6 +4605,11 @@ function scheduleCanvasPixelRatioRefresh() {
|
||||
canvasRuntime.schedulePixelRatioRefresh();
|
||||
}
|
||||
|
||||
function refreshDataGridViewportMetrics() {
|
||||
scheduleCanvasPixelRatioRefresh();
|
||||
updateDataGridTopbarWidth();
|
||||
}
|
||||
|
||||
function attachCanvasPixelRatioWatcher() {
|
||||
canvasPixelRatioMediaQueryCleanup?.();
|
||||
canvasPixelRatioMediaQueryCleanup = null;
|
||||
@@ -5131,9 +5138,9 @@ onMounted(resumeCanvasGridWork);
|
||||
onActivated(resumeCanvasGridWork);
|
||||
onMounted(() => {
|
||||
if (typeof window === "undefined") return;
|
||||
window.addEventListener("resize", scheduleCanvasPixelRatioRefresh);
|
||||
window.visualViewport?.addEventListener("resize", scheduleCanvasPixelRatioRefresh);
|
||||
window.addEventListener("dbx:ui-scale-applied", scheduleCanvasPixelRatioRefresh);
|
||||
window.addEventListener("resize", refreshDataGridViewportMetrics);
|
||||
window.visualViewport?.addEventListener("resize", refreshDataGridViewportMetrics);
|
||||
window.addEventListener("dbx:ui-scale-applied", refreshDataGridViewportMetrics);
|
||||
window.addEventListener(TABLE_DATA_GRID_COLUMN_ORDER_CHANGED_EVENT, onTableDataGridColumnOrderChanged);
|
||||
window.addEventListener("blur", clearInternalClipboardCopy);
|
||||
document.addEventListener("visibilitychange", clearInternalClipboardCopy);
|
||||
@@ -5151,9 +5158,9 @@ onUnmounted(() => {
|
||||
if (columnLayoutRefreshFrame) cancelAnimationFrame(columnLayoutRefreshFrame);
|
||||
columnLayoutRefreshFrame = 0;
|
||||
if (typeof window === "undefined") return;
|
||||
window.removeEventListener("resize", scheduleCanvasPixelRatioRefresh);
|
||||
window.visualViewport?.removeEventListener("resize", scheduleCanvasPixelRatioRefresh);
|
||||
window.removeEventListener("dbx:ui-scale-applied", scheduleCanvasPixelRatioRefresh);
|
||||
window.removeEventListener("resize", refreshDataGridViewportMetrics);
|
||||
window.visualViewport?.removeEventListener("resize", refreshDataGridViewportMetrics);
|
||||
window.removeEventListener("dbx:ui-scale-applied", refreshDataGridViewportMetrics);
|
||||
window.removeEventListener(TABLE_DATA_GRID_COLUMN_ORDER_CHANGED_EVENT, onTableDataGridColumnOrderChanged);
|
||||
window.removeEventListener("blur", clearInternalClipboardCopy);
|
||||
document.removeEventListener("visibilitychange", clearInternalClipboardCopy);
|
||||
|
||||
@@ -89,7 +89,7 @@ import * as api from "@/lib/backend/api";
|
||||
import { applyMongoGridChangesToDocument, applyMongoGridChangesToDocumentBaseline, buildMongoUpdateDocument, formatMongoShellLiteral, serializeMongoDocumentId, type MongoInputValue } from "@/lib/mongo/mongoDocumentValues";
|
||||
import type { SqlExecutionOverride } from "@/lib/sql/sqlExecutionTarget";
|
||||
import type { DataGridSortMode } from "@/lib/dataGrid/dataGridSort";
|
||||
import { DATA_GRID_COMPACT_TOPBAR_WIDTH, type DataGridReloadIntent } from "@/lib/dataGrid/dataGridToolbar";
|
||||
import { isDataGridToolbarCompact, type DataGridReloadIntent } from "@/lib/dataGrid/dataGridToolbar";
|
||||
import { useTabScroll } from "@/composables/useTabScroll";
|
||||
import { formatElapsedSeconds } from "@/lib/common/elapsedTime";
|
||||
import type { CustomSaveHandler } from "@/composables/useDataGridEditor";
|
||||
@@ -188,6 +188,9 @@ onMounted(() => {
|
||||
setTimeout(preload, 300);
|
||||
}
|
||||
window.addEventListener("dbx-refresh-active-kv-browser", onRefreshActiveKvBrowser);
|
||||
window.addEventListener("resize", updateStandaloneResultToolbarDimensions);
|
||||
window.visualViewport?.addEventListener("resize", updateStandaloneResultToolbarDimensions);
|
||||
window.addEventListener("dbx:ui-scale-applied", updateStandaloneResultToolbarDimensions);
|
||||
});
|
||||
|
||||
watch(
|
||||
@@ -208,6 +211,7 @@ const queryEditorRef = ref<InstanceType<typeof QueryEditor>>();
|
||||
const tableStructureEditorRef = ref<{ applyChanges: () => Promise<boolean> }>();
|
||||
const standaloneResultToolbarRef = ref<HTMLElement | null>(null);
|
||||
const standaloneResultToolbarWidth = ref(0);
|
||||
const standaloneResultToolbarViewportWidth = ref(0);
|
||||
const resultTabsScrollerRef = ref<HTMLElement | null>(null);
|
||||
const dataGridViewOptionsOpen = ref(false);
|
||||
const dataGridRenderMode = computed(() => settingsStore.editorSettings.dataGridRenderMode);
|
||||
@@ -417,18 +421,21 @@ const hasTabularResult = computed(() => {
|
||||
const canShowResultOutput = computed(() => hasTabularResult.value || props.activeTab.isExecuting);
|
||||
const canShowExplainOutput = computed(() => !!props.activeTab.explainPlan || !!props.activeTab.explainError || !!props.activeTab.explainTableResult || !!props.activeTab.explainTableError || props.activeTab.isExplaining === true);
|
||||
const showStandaloneResultToolbar = computed(() => activeElasticsearchJsonResponse.value || props.activeOutputView !== "result" || !props.activeTab.result || !hasTabularResult.value);
|
||||
const standaloneResultToolbarCompact = computed(() => standaloneResultToolbarWidth.value > 0 && standaloneResultToolbarWidth.value < DATA_GRID_COMPACT_TOPBAR_WIDTH);
|
||||
const standaloneResultToolbarCompact = computed(() => isDataGridToolbarCompact(standaloneResultToolbarWidth.value, standaloneResultToolbarViewportWidth.value));
|
||||
let standaloneResultToolbarResizeObserver: ResizeObserver | undefined;
|
||||
|
||||
function updateStandaloneResultToolbarDimensions() {
|
||||
standaloneResultToolbarWidth.value = standaloneResultToolbarRef.value?.clientWidth ?? 0;
|
||||
standaloneResultToolbarViewportWidth.value = typeof window === "undefined" ? 0 : window.innerWidth;
|
||||
}
|
||||
|
||||
function observeStandaloneResultToolbar() {
|
||||
standaloneResultToolbarResizeObserver?.disconnect();
|
||||
standaloneResultToolbarResizeObserver = undefined;
|
||||
const toolbar = standaloneResultToolbarRef.value;
|
||||
standaloneResultToolbarWidth.value = toolbar?.clientWidth ?? 0;
|
||||
updateStandaloneResultToolbarDimensions();
|
||||
if (toolbar && typeof ResizeObserver !== "undefined") {
|
||||
standaloneResultToolbarResizeObserver = new ResizeObserver(() => {
|
||||
standaloneResultToolbarWidth.value = toolbar.clientWidth;
|
||||
});
|
||||
standaloneResultToolbarResizeObserver = new ResizeObserver(updateStandaloneResultToolbarDimensions);
|
||||
standaloneResultToolbarResizeObserver.observe(toolbar);
|
||||
}
|
||||
}
|
||||
@@ -560,6 +567,9 @@ onUnmounted(() => {
|
||||
stopQueryRunningElapsedTimer();
|
||||
standaloneResultToolbarResizeObserver?.disconnect();
|
||||
window.removeEventListener("dbx-refresh-active-kv-browser", onRefreshActiveKvBrowser);
|
||||
window.removeEventListener("resize", updateStandaloneResultToolbarDimensions);
|
||||
window.visualViewport?.removeEventListener("resize", updateStandaloneResultToolbarDimensions);
|
||||
window.removeEventListener("dbx:ui-scale-applied", updateStandaloneResultToolbarDimensions);
|
||||
});
|
||||
|
||||
watch(
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
DATA_GRID_CONDITION_TOOLBAR_MIN_WIDTH,
|
||||
dataGridToolbarCompactBreakpoint,
|
||||
dataGridToolbarIntervalOptions,
|
||||
isDataGridToolbarCompact,
|
||||
selectDataGridToolbarAutoRefreshInterval,
|
||||
selectDataGridToolbarCopyItem,
|
||||
selectDataGridToolbarExportItem,
|
||||
@@ -28,6 +31,25 @@ function autoRefreshCapability(overrides: Partial<DataGridToolbarAutoRefreshCapa
|
||||
}
|
||||
|
||||
describe("data grid toolbar capabilities", () => {
|
||||
it("uses a viewport-relative compact breakpoint within stable bounds", () => {
|
||||
expect(dataGridToolbarCompactBreakpoint(1920)).toBe(1050);
|
||||
expect(dataGridToolbarCompactBreakpoint(1280)).toBe(960);
|
||||
expect(dataGridToolbarCompactBreakpoint(1080)).toBe(900);
|
||||
});
|
||||
|
||||
it("keeps a scaled 1080p workspace expanded while compacting a narrower pane", () => {
|
||||
expect(isDataGridToolbarCompact(1040, 1920)).toBe(true);
|
||||
expect(isDataGridToolbarCompact(1040, 1280)).toBe(false);
|
||||
expect(isDataGridToolbarCompact(900, 1280)).toBe(true);
|
||||
expect(isDataGridToolbarCompact(0, 1280)).toBe(false);
|
||||
});
|
||||
|
||||
it("preserves condition input space in embedded data grids", () => {
|
||||
expect(dataGridToolbarCompactBreakpoint(1100, DATA_GRID_CONDITION_TOOLBAR_MIN_WIDTH)).toBe(1050);
|
||||
expect(isDataGridToolbarCompact(1000, 1100, DATA_GRID_CONDITION_TOOLBAR_MIN_WIDTH)).toBe(true);
|
||||
expect(isDataGridToolbarCompact(1000, 1100)).toBe(false);
|
||||
});
|
||||
|
||||
it("does not invoke hidden or disabled actions", async () => {
|
||||
const onTrigger = vi.fn();
|
||||
|
||||
|
||||
@@ -1,4 +1,17 @@
|
||||
export const DATA_GRID_COMPACT_TOPBAR_WIDTH = 1050;
|
||||
const DATA_GRID_COMPACT_TOPBAR_MIN_WIDTH = 900;
|
||||
const DATA_GRID_COMPACT_TOPBAR_MAX_WIDTH = 1050;
|
||||
const DATA_GRID_COMPACT_TOPBAR_VIEWPORT_RATIO = 0.75;
|
||||
export const DATA_GRID_CONDITION_TOOLBAR_MIN_WIDTH = DATA_GRID_COMPACT_TOPBAR_MAX_WIDTH;
|
||||
|
||||
export function dataGridToolbarCompactBreakpoint(viewportWidth: number, minimumWidth = DATA_GRID_COMPACT_TOPBAR_MIN_WIDTH): number {
|
||||
const normalizedViewportWidth = Number.isFinite(viewportWidth) ? Math.max(0, viewportWidth) : DATA_GRID_COMPACT_TOPBAR_MAX_WIDTH;
|
||||
const normalizedMinimumWidth = Math.min(DATA_GRID_COMPACT_TOPBAR_MAX_WIDTH, Math.max(DATA_GRID_COMPACT_TOPBAR_MIN_WIDTH, minimumWidth));
|
||||
return Math.min(DATA_GRID_COMPACT_TOPBAR_MAX_WIDTH, Math.max(normalizedMinimumWidth, normalizedViewportWidth * DATA_GRID_COMPACT_TOPBAR_VIEWPORT_RATIO));
|
||||
}
|
||||
|
||||
export function isDataGridToolbarCompact(toolbarWidth: number, viewportWidth: number, minimumWidth?: number): boolean {
|
||||
return toolbarWidth > 0 && toolbarWidth < dataGridToolbarCompactBreakpoint(viewportWidth, minimumWidth);
|
||||
}
|
||||
|
||||
export type DataGridReloadIntent = "refresh";
|
||||
|
||||
|
||||
Reference in new issue
Block a user