fix(grid): improve column drag scrolling and selection
This commit is contained in:
1 parent
7eac5dd610
commit
e4b9e55883
9 files changed
+539
-16
No files matched your search
@@ -2123,6 +2123,16 @@ const gridViewportWidth = ref(0);
|
||||
let gridScrollLeftBeforeTranspose = 0;
|
||||
let gridScrollTopBeforeKeyboardTranspose: number | null = null;
|
||||
let restoreGridScrollTopAfterTranspose = false;
|
||||
|
||||
function persistDraggedColumnOrder(indexes: number[]) {
|
||||
const previousVisibleColumnIndexes = [...visibleColumnIndexes.value];
|
||||
persistColumnOrder(indexes);
|
||||
selection.remapColumnSelection(
|
||||
previousVisibleColumnIndexes,
|
||||
indexes.filter((index) => !hiddenColumnIndexes.value.has(index)),
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
renderedColumnOffsets,
|
||||
horizontalColumnWindow,
|
||||
@@ -2147,14 +2157,19 @@ const {
|
||||
viewportWidth: gridViewportWidth,
|
||||
rowNumberWidth,
|
||||
headerRef,
|
||||
getScrollElement: gridScrollerElement,
|
||||
orderedColumnIndexes: orderedDisplayableColumnIndexes,
|
||||
hiddenColumnIndexes,
|
||||
getIsResizing,
|
||||
onResizeStart,
|
||||
onCanvasMouseLeave,
|
||||
onCanvasDrawSchedule: scheduleCanvasDraw,
|
||||
onHorizontalScroll: (scroller) => {
|
||||
updateGridHorizontalViewport(scroller);
|
||||
if (headerRef.value) headerRef.value.scrollLeft = scroller.scrollLeft;
|
||||
},
|
||||
onRefreshMetrics: scheduleColumnLayoutRefresh,
|
||||
onPersistColumnOrder: persistColumnOrder,
|
||||
onPersistColumnOrder: persistDraggedColumnOrder,
|
||||
frozenColumnCount,
|
||||
});
|
||||
|
||||
|
||||
@@ -20,9 +20,11 @@ const props = withDefaults(
|
||||
defineProps<{
|
||||
grid?: DataGridColumnLayoutHandle;
|
||||
triggerClass?: string;
|
||||
compact?: boolean;
|
||||
}>(),
|
||||
{
|
||||
triggerClass: "",
|
||||
compact: false,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -299,14 +301,14 @@ onBeforeUnmount(resetColumnDragState);
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
class="h-5 shrink-0 gap-1 px-1.5 text-xs text-foreground hover:bg-accent"
|
||||
:class="[triggerClass, { 'bg-accent text-foreground': (grid?.hiddenColumnCount ?? 0) > 0 }]"
|
||||
:class="[triggerClass, compact ? 'w-6 gap-0 px-0' : '', { 'bg-accent text-foreground': (grid?.hiddenColumnCount ?? 0) > 0 }]"
|
||||
:disabled="!grid"
|
||||
:title="t('grid.columnVisibility')"
|
||||
:aria-label="t('grid.columnVisibility')"
|
||||
>
|
||||
<Columns3 class="h-3.5 w-3.5" />
|
||||
{{ t("grid.columnVisibility") }}
|
||||
<span v-if="(grid?.hiddenColumnCount ?? 0) > 0" class="tabular-nums"> {{ grid?.visibleColumnCount }}/{{ grid?.displayableColumnCount }} </span>
|
||||
<span v-if="!compact">{{ t("grid.columnVisibility") }}</span>
|
||||
<span v-if="!compact && (grid?.hiddenColumnCount ?? 0) > 0" class="tabular-nums"> {{ grid?.visibleColumnCount }}/{{ grid?.displayableColumnCount }} </span>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent align="end" class="w-72 max-w-[calc(100vw-2rem)] gap-0 overflow-hidden rounded-md border bg-popover p-0 text-popover-foreground shadow-xl" @click.stop @keydown.stop>
|
||||
|
||||
+21
@@ -1,5 +1,7 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { createApp, nextTick, type App } from "vue";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import i18n from "@/i18n";
|
||||
@@ -39,6 +41,8 @@ vi.mock("@/components/ui/button", async () => {
|
||||
|
||||
import DataGridColumnLayoutPopover from "../DataGridColumnLayoutPopover.vue";
|
||||
|
||||
const contentAreaSource = readFileSync(path.resolve(process.cwd(), "apps/desktop/src/components/layout/ContentArea.vue"), "utf8");
|
||||
|
||||
const mountedApps: Array<{ app: App; host: HTMLElement }> = [];
|
||||
|
||||
function columnLayoutOptions(itemCount: number): DataGridColumnLayoutOption[] {
|
||||
@@ -137,6 +141,23 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
describe("data grid column layout popover", () => {
|
||||
it("is available from the query result toolbar and keeps a labelled compact trigger", async () => {
|
||||
expect(contentAreaSource).toContain('<DataGridColumnLayoutPopover :grid="dataGridRef" :compact="compact" />');
|
||||
|
||||
const gridState = createGrid(4);
|
||||
const host = document.createElement("div");
|
||||
document.body.append(host);
|
||||
const app = createApp(DataGridColumnLayoutPopover, { grid: gridState.grid, compact: true });
|
||||
app.use(i18n);
|
||||
app.mount(host);
|
||||
mountedApps.push({ app, host });
|
||||
await nextTick();
|
||||
|
||||
const trigger = [...host.querySelectorAll<HTMLButtonElement>("button")].find((button) => button.getAttribute("aria-label") === i18n.global.t("grid.columnVisibility"));
|
||||
expect(trigger?.className).toContain("w-6");
|
||||
expect(trigger?.textContent?.trim()).toBe("");
|
||||
});
|
||||
|
||||
it("renders only the visible field window plus a bounded buffer", () => {
|
||||
const window = dataGridColumnLayoutVirtualWindow({
|
||||
itemCount: 500,
|
||||
|
||||
@@ -1622,6 +1622,7 @@ defineExpose({ focusSearch, refreshData, refreshQueryEditorCompletionCache, hand
|
||||
</template>
|
||||
</template>
|
||||
<template #result-toolbar-actions="{ compact }">
|
||||
<DataGridColumnLayoutPopover :grid="dataGridRef" :compact="compact" />
|
||||
<QueryResultToolbarActions
|
||||
:active-view="activeOutputView"
|
||||
:can-show-explain="canShowExplainOutput"
|
||||
|
||||
Reference in new issue
Block a user