From 27c60f0d30421916922e9d4799ee4f378fe53d29 Mon Sep 17 00:00:00 2001 From: zipg Date: Mon, 13 Jul 2026 17:33:36 +0800 Subject: [PATCH] fix(grid): keep BLOB download menu anchored --- apps/desktop/src/components/grid/DataGrid.vue | 15 ++++++++++++++- .../src/lib/dataGrid/binaryCellDownload.ts | 9 +++++++++ packages/app-tests/binaryCellDownload.test.ts | 18 +++++++++++++++++- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/components/grid/DataGrid.vue b/apps/desktop/src/components/grid/DataGrid.vue index 38e3d52ead..7908e89ecb 100644 --- a/apps/desktop/src/components/grid/DataGrid.vue +++ b/apps/desktop/src/components/grid/DataGrid.vue @@ -126,7 +126,18 @@ import { canApplyGridSelectionValue, canDeleteGridRowItem, canEditGridCellDetail import { displayCellValue, firstLineCellDisplayValue, type CellValue } from "@/lib/dataGrid/cellValue"; import { getApplicablePreviewActions } from "@/lib/dataGrid/resultPreviewRegistry"; import "@/lib/dataGrid/geometryMapPreview"; -import { BINARY_CELL_DOWNLOAD_MODES, binaryCellDisplayText, binaryCellDownloadFileName, binaryCellDownloadPayload, canDownloadBinaryCellValue, downloadBinaryCellPayload, isBinaryCellColumnType, parseBinaryCellBytes, type BinaryCellDownloadMode } from "@/lib/dataGrid/binaryCellDownload"; +import { + BINARY_CELL_DOWNLOAD_MODES, + binaryCellDisplayText, + binaryCellDownloadFileName, + binaryCellDownloadPayload, + canDownloadBinaryCellValue, + downloadBinaryCellPayload, + isBinaryCellColumnType, + parseBinaryCellBytes, + retainBinaryCellDownloadMenuForHover, + type BinaryCellDownloadMode, +} from "@/lib/dataGrid/binaryCellDownload"; import { buildBinaryHexViewRows } from "@/lib/dataGrid/binaryHexViewer"; import { canFormatCellDetailJson, cellDetailEditorText, compactJsonText, defaultCellDetailTab, formatJsonText, isGeometryColumnType, linkedCellDetailTarget, looksLikeJsonContainerText, valueEditorActions, visibleCellDetailTabs, type CellDetailTab } from "@/lib/dataGrid/cellDetailPresentation"; import { renderWktOnCanvas, isHexGeometry } from "@/lib/dataGrid/geometryPreview"; @@ -4743,6 +4754,7 @@ const selectionSummarySumText = computed(() => { const isMultiRow = computed(() => multiRowCount.value > 1); function onCellMouseenter(rowIndex: number, visibleColIdx: number, actualColIdx: number) { + quickDownloadMenuCell.value = retainBinaryCellDownloadMenuForHover(quickDownloadMenuCell.value, { rowIndex, col: actualColIdx }); if (!isScrolling.value) hoveredDetailCell.value = { rowIndex, col: actualColIdx }; extendCellSelection(rowIndex, visibleColIdx); } @@ -6686,6 +6698,7 @@ function transposeCellIsSelected(rowIndex: number, actualColIdx: number) { } function onTransposeCellMouseenter(rowIndex: number, actualColIdx: number) { + quickDownloadMenuCell.value = retainBinaryCellDownloadMenuForHover(quickDownloadMenuCell.value, { rowIndex, col: actualColIdx }); if (isScrolling.value) return; hoveredDetailCell.value = { rowIndex, col: actualColIdx }; } diff --git a/apps/desktop/src/lib/dataGrid/binaryCellDownload.ts b/apps/desktop/src/lib/dataGrid/binaryCellDownload.ts index 92d48c5b98..c7be5ab6b4 100644 --- a/apps/desktop/src/lib/dataGrid/binaryCellDownload.ts +++ b/apps/desktop/src/lib/dataGrid/binaryCellDownload.ts @@ -3,6 +3,11 @@ import { isTauriRuntime } from "@/lib/backend/tauriRuntime"; export type BinaryCellDownloadMode = "binary" | "utf8" | "gbk"; +export interface BinaryCellPosition { + rowIndex: number; + col: number; +} + export interface BinaryCellDownloadPayload { data: Uint8Array | string; mimeType: string; @@ -17,6 +22,10 @@ export interface BinaryCellDownloadResult { export const BINARY_CELL_DOWNLOAD_MODES: BinaryCellDownloadMode[] = ["binary", "utf8", "gbk"]; +export function retainBinaryCellDownloadMenuForHover(openCell: BinaryCellPosition | null, hoveredCell: BinaryCellPosition): BinaryCellPosition | null { + return openCell?.rowIndex === hoveredCell.rowIndex && openCell.col === hoveredCell.col ? openCell : null; +} + const HEX_VALUE_RE = /^(?:0[xX]|\\x)([0-9a-fA-F\s]+)$/; const BARE_HEX_RE = /^[0-9a-fA-F\s]+$/; const HEX_ESCAPE_RE = /^(?:\\x[0-9a-fA-F]{2}|\s)+$/; diff --git a/packages/app-tests/binaryCellDownload.test.ts b/packages/app-tests/binaryCellDownload.test.ts index 7df5444564..c5f4036595 100644 --- a/packages/app-tests/binaryCellDownload.test.ts +++ b/packages/app-tests/binaryCellDownload.test.ts @@ -1,7 +1,8 @@ import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; import { test } from "vitest"; -import { binaryCellDisplayText, binaryCellDownloadFileName, binaryCellDownloadPayload, canDownloadBinaryCellValue, isBinaryCellColumnType, parseBinaryCellBytes, parseBinaryCellHexValue } from "../../apps/desktop/src/lib/dataGrid/binaryCellDownload.ts"; +import { binaryCellDisplayText, binaryCellDownloadFileName, binaryCellDownloadPayload, canDownloadBinaryCellValue, isBinaryCellColumnType, parseBinaryCellBytes, parseBinaryCellHexValue, retainBinaryCellDownloadMenuForHover } from "../../apps/desktop/src/lib/dataGrid/binaryCellDownload.ts"; test("parseBinaryCellHexValue accepts 0x and \\x prefixed hex values", () => { assert.deepEqual(Array.from(parseBinaryCellHexValue("0X48656c6c6f") ?? []), [72, 101, 108, 108, 111]); @@ -28,6 +29,21 @@ test("binary cell download detects common blob column types", () => { assert.equal(isBinaryCellColumnType("varchar"), false); }); +test("binary cell download menu closes when hover moves to another cell", () => { + const openCell = { rowIndex: 2, col: 4 }; + + assert.equal(retainBinaryCellDownloadMenuForHover(openCell, { rowIndex: 3, col: 4 }), null); + assert.equal(retainBinaryCellDownloadMenuForHover(openCell, { rowIndex: 2, col: 5 }), null); + assert.equal(retainBinaryCellDownloadMenuForHover(openCell, { rowIndex: 2, col: 4 }), openCell); +}); + +test("transpose cell hover also clears a different binary download menu", () => { + const source = readFileSync("apps/desktop/src/components/grid/DataGrid.vue", "utf8"); + const handler = source.match(/function onTransposeCellMouseenter\([^]*?\n\}/)?.[0] ?? ""; + + assert.match(handler, /retainBinaryCellDownloadMenuForHover\(quickDownloadMenuCell\.value, \{ rowIndex, col: actualColIdx \}\)/); +}); + test("canDownloadBinaryCellValue allows displayed binary hex strings", () => { assert.equal(canDownloadBinaryCellValue("0x89504e47", "BLOB"), true); assert.equal(canDownloadBinaryCellValue("0x89504e47"), true);