feat(grid): configure cell detail button visibility

Closes #5593
This commit is contained in:
t8y2 committed 2026-08-24 19:54:35 +00:00
1 parent 3232195e1f
commit 68b0ab0476
17 files changed
+146 -9

No files matched your search

@@ -369,6 +369,7 @@ const editDataGridQuickEntry = ref(settingsStore.editorSettings.dataGridQuickEnt
const editDataGridFilterEditorView = ref<DataGridFilterEditorView>(settingsStore.editorSettings.dataGridFilterEditorView);
const editDataGridTextFilterPanelHeight = ref(settingsStore.editorSettings.dataGridTextFilterPanelHeight);
const editDataGridAutoTransposeSingleRow = ref(settingsStore.editorSettings.dataGridAutoTransposeSingleRow);
const editDataGridCellDetailButtonVisible = ref(settingsStore.editorSettings.dataGridCellDetailButtonVisible);
const editPageSize = ref(settingsStore.editorSettings.pageSize);
const editTableOpenPageSize = ref(settingsStore.editorSettings.tableOpenPageSize);
const editQueryResultMaxRowsEnabled = ref(settingsStore.editorSettings.queryResultMaxRowsEnabled);
@@ -557,6 +558,7 @@ function currentEditorSettingsDraft(): EditorSettingsDraft {
dataGridFilterEditorView: editDataGridFilterEditorView.value,
dataGridTextFilterPanelHeight: editDataGridTextFilterPanelHeight.value,
dataGridAutoTransposeSingleRow: editDataGridAutoTransposeSingleRow.value,
dataGridCellDetailButtonVisible: editDataGridCellDetailButtonVisible.value,
flatteningMultiLineText: editFlatteningMultiLineText.value,
pageSize: editPageSize.value,
tableOpenPageSize: editTableOpenPageSize.value,
@@ -845,6 +847,7 @@ function syncEditorSettingsDraftFromStore() {
editDataGridFilterEditorView.value = settingsStore.editorSettings.dataGridFilterEditorView;
editDataGridTextFilterPanelHeight.value = settingsStore.editorSettings.dataGridTextFilterPanelHeight;
editDataGridAutoTransposeSingleRow.value = settingsStore.editorSettings.dataGridAutoTransposeSingleRow;
editDataGridCellDetailButtonVisible.value = settingsStore.editorSettings.dataGridCellDetailButtonVisible;
editFlatteningMultiLineText.value = settingsStore.editorSettings.flatteningMultiLineText;
editPageSize.value = settingsStore.editorSettings.pageSize;
editTableOpenPageSize.value = settingsStore.editorSettings.tableOpenPageSize;
@@ -1139,6 +1142,7 @@ function resetDefaultsForTab(tab: SettingsCategory) {
editDataGridFilterEditorView.value = DEFAULT_EDITOR_SETTINGS.dataGridFilterEditorView;
editDataGridTextFilterPanelHeight.value = DEFAULT_EDITOR_SETTINGS.dataGridTextFilterPanelHeight;
editDataGridAutoTransposeSingleRow.value = DEFAULT_EDITOR_SETTINGS.dataGridAutoTransposeSingleRow;
editDataGridCellDetailButtonVisible.value = DEFAULT_EDITOR_SETTINGS.dataGridCellDetailButtonVisible;
editFlatteningMultiLineText.value = DEFAULT_EDITOR_SETTINGS.flatteningMultiLineText;
editPageSize.value = DEFAULT_EDITOR_SETTINGS.pageSize;
editTableOpenPageSize.value = DEFAULT_EDITOR_SETTINGS.tableOpenPageSize;
@@ -1218,6 +1222,7 @@ function resetAllDefaults() {
editDataGridFilterEditorView.value = DEFAULT_EDITOR_SETTINGS.dataGridFilterEditorView;
editDataGridTextFilterPanelHeight.value = DEFAULT_EDITOR_SETTINGS.dataGridTextFilterPanelHeight;
editDataGridAutoTransposeSingleRow.value = DEFAULT_EDITOR_SETTINGS.dataGridAutoTransposeSingleRow;
editDataGridCellDetailButtonVisible.value = DEFAULT_EDITOR_SETTINGS.dataGridCellDetailButtonVisible;
editFlatteningMultiLineText.value = DEFAULT_EDITOR_SETTINGS.flatteningMultiLineText;
editPageSize.value = DEFAULT_EDITOR_SETTINGS.pageSize;
editTableOpenPageSize.value = DEFAULT_EDITOR_SETTINGS.tableOpenPageSize;
@@ -5305,6 +5310,17 @@ onUnmounted(() => {
</div>
<Switch id="data-grid-auto-transpose-single-row" v-model="editDataGridAutoTransposeSingleRow" />
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="data-grid-cell-detail-button-visible">
{{ t("settings.dataGridCellDetailButtonVisible") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.dataGridCellDetailButtonVisibleDescription") }}
</p>
</div>
<Switch id="data-grid-cell-detail-button-visible" v-model="editDataGridCellDetailButtonVisible" />
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="flattening-multi-line">
@@ -362,6 +362,7 @@ const slots = useSlots();
const connectionStore = useConnectionStore();
const queryStore = useQueryStore();
const settingsStore = useSettingsStore();
const cellDetailButtonEnabled = computed(() => settingsStore.editorSettings.dataGridCellDetailButtonVisible);
const tableFontSize = computed(() => settingsStore.editorSettings.tableFontSize);
const rowNumberWidth = ref(DATA_GRID_ROW_NUM_WIDTH);
const largeValueResolutionVersion = ref(0);
@@ -7296,7 +7297,8 @@ const canvasDetailButtonCell = computed(() => {
const visibleRight = viewportWidth > 0 ? Math.min(viewportWidth, rect.left + rect.width) : rect.left + rect.width;
const canQuickDownload = canQuickDownloadCellValue(target.rowIndex, target.col);
const foreignKey = canvasCellForeignKey(target.rowIndex, target.col);
const minWidth = canvasDataGridActionOverlayWidth(canQuickDownload, !!foreignKey) + 2;
if (!cellDetailButtonEnabled.value && !canQuickDownload && !foreignKey) return null;
const minWidth = canvasDataGridActionOverlayWidth(canQuickDownload, !!foreignKey, cellDetailButtonEnabled.value) + 2;
if (rect.top < 0 || rect.top > viewportHeight - 1 || visibleRight - visibleLeft < minWidth) return null;
return {
rowIndex: target.rowIndex,
@@ -7311,7 +7313,7 @@ const canvasDetailButtonCell = computed(() => {
const canvasDetailButtonStyle = computed(() => {
const cell = canvasDetailButtonCell.value;
if (!cell) return {};
const actionWidth = canvasDataGridActionOverlayWidth(cell.canQuickDownload, !!cell.foreignKey);
const actionWidth = canvasDataGridActionOverlayWidth(cell.canQuickDownload, !!cell.foreignKey, cellDetailButtonEnabled.value);
const edgeGap = 6;
return {
left: `${Math.max(rowNumberWidth.value, cell.rect.left + cell.rect.width - actionWidth - edgeGap)}px`,
@@ -7325,7 +7327,7 @@ const canvasRightAlignedActionCell = computed(() => {
return {
rowIndex: cell.rowIndex,
visibleColIdx: cell.visibleColIdx,
reservedWidth: canvasDataGridActionReservedWidth(cell.canQuickDownload, !!cell.foreignKey),
reservedWidth: canvasDataGridActionReservedWidth(cell.canQuickDownload, !!cell.foreignKey, cellDetailButtonEnabled.value),
};
});
@@ -7412,6 +7414,7 @@ watch(
hoveredDetailCell,
detailCell,
showCellDetail,
cellDetailButtonEnabled,
editingCell,
frozenColumnCount,
saveError,
@@ -12083,6 +12086,7 @@ function currentGridContextMenuItems(): ContextMenuItem[] {
</template>
</LightDropdownMenu>
<button
v-if="cellDetailButtonEnabled"
class="flex h-5 w-5 items-center justify-center rounded bg-background/90 text-muted-foreground shadow-sm ring-1 ring-border hover:text-foreground"
:title="t('grid.cellDetails')"
@mousedown.stop
@@ -12816,6 +12820,7 @@ function currentGridContextMenuItems(): ContextMenuItem[] {
<ArrowUpRight class="h-3 w-3" />
</button>
<button
v-if="cellDetailButtonEnabled"
class="flex h-5 w-5 items-center justify-center rounded bg-background/90 text-muted-foreground shadow-sm ring-1 ring-border hover:text-foreground"
:title="t('grid.cellDetails')"
@mousedown.stop
@@ -13031,6 +13036,7 @@ function currentGridContextMenuItems(): ContextMenuItem[] {
</template>
</LightDropdownMenu>
<button
v-if="cellDetailButtonEnabled"
class="flex h-5 w-5 items-center justify-center rounded bg-background/90 text-muted-foreground shadow-sm ring-1 ring-border hover:text-foreground"
:title="t('grid.cellDetails')"
@mousedown.stop
@@ -4,6 +4,29 @@ import { describe, expect, it } from "vitest";
const dataGridSource = readFileSync(new URL("../DataGrid.vue", import.meta.url), "utf8");
describe("DataGrid cell detail selection", () => {
it("gates only the three hover Info buttons with the visibility setting", () => {
expect(dataGridSource).toContain("const cellDetailButtonEnabled = computed(() => settingsStore.editorSettings.dataGridCellDetailButtonVisible);");
expect(dataGridSource.match(/v-if="cellDetailButtonEnabled"/g)).toHaveLength(3);
expect(dataGridSource.match(/v-if="cellDetailButtonVisible\(/g)).toHaveLength(2);
const hoverVisibility = dataGridSource.match(/function cellDetailButtonVisible[\s\S]*?\n\}/)?.[0];
expect(hoverVisibility).toBeDefined();
expect(hoverVisibility).not.toContain("dataGridCellDetailButtonVisible");
});
it("removes the Canvas action overlay and reservation only when no action remains", () => {
expect(dataGridSource).toContain("if (!cellDetailButtonEnabled.value && !canQuickDownload && !foreignKey) return null;");
expect(dataGridSource).toContain("canvasDataGridActionOverlayWidth(canQuickDownload, !!foreignKey, cellDetailButtonEnabled.value)");
expect(dataGridSource).toContain("canvasDataGridActionOverlayWidth(cell.canQuickDownload, !!cell.foreignKey, cellDetailButtonEnabled.value)");
expect(dataGridSource).toContain("canvasDataGridActionReservedWidth(cell.canQuickDownload, !!cell.foreignKey, cellDetailButtonEnabled.value)");
expect(dataGridSource).toMatch(/watch\([\s\S]*?cellDetailButtonEnabled,[\s\S]*?scheduleCanvasDraw/);
});
it("keeps context-menu details and refresh restoration independent of the hover setting", () => {
expect(dataGridSource).toContain("cellDetails: openContextCellDetailDialog");
expect(dataGridSource).toContain("if (cellDialog) openCellDetailDialog(cellDialog.rowIndex, cellDialog.col);");
});
it("resynchronizes the open detail after a mouse selection gesture finishes", () => {
expect(dataGridSource).toContain("watch([selectedRange, showCellDetail, isEditingDetail, isSelectingCells]");
expect(dataGridSource).toContain("if (isSelectingCells.value) return;");