feat(grid): improve cell detail panel
This commit is contained in:
parent
4c7205abe7
commit
16724074c0
|
|
@ -4923,6 +4923,7 @@ const dialogCellDetail = computed(() => {
|
|||
});
|
||||
|
||||
const cellDetailJsonFormatted = computed(() => settingsStore.editorSettings.cellDetailJsonFormatted);
|
||||
const cellDetailMetadataCollapsed = computed(() => settingsStore.editorSettings.cellDetailMetadataCollapsed);
|
||||
const sideDetailJsonView = computed(() => cellDetailJsonFormatted.value && !!activeCellDetail.value?.formattedJson);
|
||||
const cellDetailJsonView = computed(() => cellDetailJsonFormatted.value && !!dialogCellDetail.value?.formattedJson);
|
||||
|
||||
|
|
@ -5149,7 +5150,6 @@ const detailTemporalEditorConfig = computed(() => {
|
|||
const detail = activeCellDetail.value;
|
||||
return detail ? temporalEditorConfigForColumn(detail.colIndex) : undefined;
|
||||
});
|
||||
const sideDetailPrioritizesValue = computed(() => !cellDetailPanelIsBottom.value && !isEditingDetail.value && !!activeCellDetail.value?.formattedJson);
|
||||
const sideDetailValueFillsHeight = computed(() => cellDetailPanelIsBottom.value || isEditingDetail.value || (!cellDetailPanelIsBottom.value && !activeCellDetail.value?.imagePreviewUrl));
|
||||
const sideJsonPreviewText = computed(() => {
|
||||
const detail = activeCellDetail.value;
|
||||
|
|
@ -5354,6 +5354,10 @@ function toggleCellDetailJsonFormatted() {
|
|||
settingsStore.updateEditorSettings({ cellDetailJsonFormatted: !cellDetailJsonFormatted.value });
|
||||
}
|
||||
|
||||
function toggleCellDetailMetadataCollapsed() {
|
||||
settingsStore.updateEditorSettings({ cellDetailMetadataCollapsed: !cellDetailMetadataCollapsed.value });
|
||||
}
|
||||
|
||||
function startDetailEdit() {
|
||||
const detail = activeCellDetail.value;
|
||||
if (!detail || !detail.isEditable) return;
|
||||
|
|
@ -10586,31 +10590,22 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
|
|||
@contextmenu="onDrawerContextMenu"
|
||||
>
|
||||
<div v-if="!cellDetailPanelIsBottom" class="absolute left-0 top-0 bottom-0 z-20 w-1.5 -translate-x-1/2 cursor-col-resize hover:bg-primary/30" @mousedown.prevent="onDetailResizeStart" />
|
||||
<div v-else class="absolute left-0 right-0 top-0 z-20 h-1.5 -translate-y-1/2 cursor-row-resize hover:bg-primary/30" @mousedown.prevent="onDetailResizeStart" />
|
||||
<div class="h-9 flex items-center gap-2 px-3 border-b shrink-0 bg-muted/20">
|
||||
<Info class="w-3.5 h-3.5 text-muted-foreground" />
|
||||
<span class="text-xs font-medium flex-1 min-w-0 truncate">{{ t("grid.cellDetails") }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5" :title="cellDetailPanelIsBottom ? t('grid.cellDetailLayoutRight') : t('grid.cellDetailLayoutBottom')" @click="toggleCellDetailPanelLayout">
|
||||
<PanelRight v-if="cellDetailPanelIsBottom" class="w-3 h-3" />
|
||||
<PanelBottom v-else class="w-3 h-3" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5" :title="t('grid.openCellDetailsDialog')" @click="openActiveCellDetailDialog">
|
||||
<Maximize2 class="w-3 h-3" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5" :title="t('grid.openRowDetailsDialog')" @click="openActiveRowDetailDialog">
|
||||
<ListTree class="w-3 h-3" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5" :title="t('grid.openColumnDetailsDialog')" @click="openActiveColumnDetailDialog">
|
||||
<TableProperties class="w-3 h-3" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5" @click="closeCellDetails">
|
||||
<X class="w-3 h-3" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div v-else class="data-grid-detail-resize-handle data-grid-detail-resize-handle--bottom absolute left-0 right-0 top-0 z-20 h-2 -translate-y-1/2 cursor-row-resize" @mousedown.prevent="onDetailResizeStart" />
|
||||
<Tabs v-model="activeCellDetailTab" class="flex-1 min-h-0 gap-0">
|
||||
<div class="shrink-0 border-b px-3 py-2">
|
||||
<TabsList class="grid h-7 w-full p-0.5" :class="activeCellDetailTabsGridClass">
|
||||
<div class="h-9 flex items-center gap-2 px-3 border-b shrink-0 bg-muted/20">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-5 w-5 shrink-0"
|
||||
:title="cellDetailMetadataCollapsed ? t('grid.expandCellDetailMetadata') : t('grid.collapseCellDetailMetadata')"
|
||||
:aria-label="cellDetailMetadataCollapsed ? t('grid.expandCellDetailMetadata') : t('grid.collapseCellDetailMetadata')"
|
||||
:aria-expanded="!cellDetailMetadataCollapsed"
|
||||
@click="toggleCellDetailMetadataCollapsed"
|
||||
>
|
||||
<ChevronRight v-if="cellDetailMetadataCollapsed" class="w-3 h-3" />
|
||||
<ChevronDown v-else class="w-3 h-3" />
|
||||
</Button>
|
||||
<TabsList class="grid h-7 min-w-0 flex-1 p-0.5" :class="activeCellDetailTabsGridClass">
|
||||
<TabsTrigger value="details" class="h-6 text-xs">{{ t("grid.cellDetails") }}</TabsTrigger>
|
||||
<TabsTrigger v-if="activeCellDetailTabs.includes('hexViewer')" value="hexViewer" class="h-6 text-xs">
|
||||
{{ t("grid.hexViewer") }}
|
||||
|
|
@ -10619,11 +10614,29 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
|
|||
{{ t("grid.valueEditor") }}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<div class="ml-auto flex shrink-0 items-center gap-1">
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5" :title="cellDetailPanelIsBottom ? t('grid.cellDetailLayoutRight') : t('grid.cellDetailLayoutBottom')" @click="toggleCellDetailPanelLayout">
|
||||
<PanelRight v-if="cellDetailPanelIsBottom" class="w-3 h-3" />
|
||||
<PanelBottom v-else class="w-3 h-3" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5" :title="t('grid.openCellDetailsDialog')" @click="openActiveCellDetailDialog">
|
||||
<Maximize2 class="w-3 h-3" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5" :title="t('grid.openRowDetailsDialog')" @click="openActiveRowDetailDialog">
|
||||
<ListTree class="w-3 h-3" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5" :title="t('grid.openColumnDetailsDialog')" @click="openActiveColumnDetailDialog">
|
||||
<TableProperties class="w-3 h-3" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5" @click="closeCellDetails">
|
||||
<X class="w-3 h-3" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TabsContent value="details" class="m-0 min-h-0 flex-1 flex flex-col">
|
||||
<div data-native-clipboard class="flex-1 min-h-0 overflow-auto px-3 pt-3 text-xs" :class="[sideDetailValueFillsHeight ? 'flex flex-col gap-3' : 'space-y-3', isEditingDetail && !cellDetailPanelIsBottom ? 'pb-1' : 'pb-3']">
|
||||
<div v-if="cellDetailPanelIsBottom" class="grid grid-cols-[minmax(180px,1.6fr)_repeat(4,minmax(74px,0.55fr))_minmax(160px,1fr)] gap-3 rounded border bg-muted/20 p-2">
|
||||
<div v-if="cellDetailPanelIsBottom && !cellDetailMetadataCollapsed" class="grid grid-cols-[minmax(180px,1.6fr)_repeat(4,minmax(74px,0.55fr))_minmax(160px,1fr)] gap-3 rounded border bg-muted/20 p-2">
|
||||
<div class="min-w-0 space-y-1">
|
||||
<div class="text-muted-foreground">{{ t("grid.columnName") }}</div>
|
||||
<div class="truncate font-medium" :title="activeCellDetail.column">
|
||||
|
|
@ -10655,7 +10668,7 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-else-if="!isEditingDetail && !sideDetailPrioritizesValue">
|
||||
<template v-else-if="!cellDetailMetadataCollapsed && !isEditingDetail">
|
||||
<div class="space-y-1">
|
||||
<div class="text-muted-foreground">{{ t("grid.columnName") }}</div>
|
||||
<div class="font-medium break-all">{{ activeCellDetail.column }}</div>
|
||||
|
|
@ -11912,6 +11925,29 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
|
|||
background: var(--data-grid-scrollbar-thumb-hover);
|
||||
}
|
||||
|
||||
.data-grid-detail-resize-handle--bottom {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.data-grid-detail-resize-handle--bottom::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 42px;
|
||||
height: 2px;
|
||||
border-radius: 999px;
|
||||
background: var(--data-grid-scrollbar-thumb-hover);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transform: translate(-50%, -50%);
|
||||
transition: opacity 120ms ease;
|
||||
}
|
||||
|
||||
.detail-drawer-resizing > .data-grid-detail-resize-handle--bottom::after {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.data-grid-vertical-scrollbar {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
|
|
|
|||
|
|
@ -903,6 +903,8 @@ export default {
|
|||
cellDetails: "Cell Details",
|
||||
cellDetailLayoutBottom: "Move to Bottom",
|
||||
cellDetailLayoutRight: "Move to Right",
|
||||
collapseCellDetailMetadata: "Collapse cell metadata",
|
||||
expandCellDetailMetadata: "Expand cell metadata",
|
||||
openCellDetailsDialog: "Open Cell Details",
|
||||
rowDetails: "Row Details",
|
||||
rowDetailsFor: "Row {row} Details",
|
||||
|
|
|
|||
|
|
@ -846,6 +846,8 @@ export default withEnglishFallback({
|
|||
cellDetails: "Detalles de celda",
|
||||
cellDetailLayoutBottom: "Mover abajo",
|
||||
cellDetailLayoutRight: "Mover a la derecha",
|
||||
collapseCellDetailMetadata: "Contraer metadatos de celda",
|
||||
expandCellDetailMetadata: "Expandir metadatos de celda",
|
||||
openCellDetailsDialog: "Abrir detalles de celda",
|
||||
rowDetails: "Detalles de fila",
|
||||
rowDetailsFor: "Detalles de fila {row}",
|
||||
|
|
|
|||
|
|
@ -844,6 +844,8 @@ export default withEnglishFallback({
|
|||
cellDetails: "Dettagli Cella",
|
||||
cellDetailLayoutBottom: "Sposta in Basso",
|
||||
cellDetailLayoutRight: "Sposta a Destra",
|
||||
collapseCellDetailMetadata: "Comprimi metadati cella",
|
||||
expandCellDetailMetadata: "Espandi metadati cella",
|
||||
openCellDetailsDialog: "Apri Dettagli Cella",
|
||||
rowDetails: "Dettagli Riga",
|
||||
rowDetailsFor: "Dettagli Riga {row}",
|
||||
|
|
|
|||
|
|
@ -841,6 +841,8 @@ export default withEnglishFallback({
|
|||
cellDetails: "セル詳細",
|
||||
cellDetailLayoutBottom: "下部に表示",
|
||||
cellDetailLayoutRight: "右側に表示",
|
||||
collapseCellDetailMetadata: "セルのメタ情報を折りたたむ",
|
||||
expandCellDetailMetadata: "セルのメタ情報を展開",
|
||||
openCellDetailsDialog: "セル詳細を開く",
|
||||
rowDetails: "行詳細",
|
||||
rowDetailsFor: "行 {row} の詳細",
|
||||
|
|
|
|||
|
|
@ -846,6 +846,8 @@ export default withEnglishFallback({
|
|||
cellDetails: "Detalhes da Célula",
|
||||
cellDetailLayoutBottom: "Mover para Baixo",
|
||||
cellDetailLayoutRight: "Mover para a Direita",
|
||||
collapseCellDetailMetadata: "Recolher metadados da célula",
|
||||
expandCellDetailMetadata: "Expandir metadados da célula",
|
||||
openCellDetailsDialog: "Abrir Detalhes da Célula",
|
||||
rowDetails: "Detalhes da Linha",
|
||||
rowDetailsFor: "Detalhes da Linha {row}",
|
||||
|
|
|
|||
|
|
@ -904,6 +904,8 @@ export default withEnglishFallback({
|
|||
cellDetails: "单元格详情",
|
||||
cellDetailLayoutBottom: "移动到底部",
|
||||
cellDetailLayoutRight: "移动到右侧",
|
||||
collapseCellDetailMetadata: "收起单元格元信息",
|
||||
expandCellDetailMetadata: "展开单元格元信息",
|
||||
openCellDetailsDialog: "单元格详情",
|
||||
rowDetails: "行详情",
|
||||
rowDetailsFor: "第 {row} 行详情",
|
||||
|
|
|
|||
|
|
@ -845,6 +845,8 @@ export default withEnglishFallback({
|
|||
cellDetails: "儲存格詳情",
|
||||
cellDetailLayoutBottom: "移到底部",
|
||||
cellDetailLayoutRight: "移到右側",
|
||||
collapseCellDetailMetadata: "收合儲存格中繼資訊",
|
||||
expandCellDetailMetadata: "展開儲存格中繼資訊",
|
||||
openCellDetailsDialog: "儲存格詳情",
|
||||
rowDetails: "資料詳情",
|
||||
rowDetailsFor: "第 {row} 筆資料詳情",
|
||||
|
|
|
|||
|
|
@ -79,6 +79,11 @@ describe("normalizeEditorSettings", () => {
|
|||
expect(normalizeEditorSettings({ dataGridSearchMode: "invalid" as any }).dataGridSearchMode).toBe("filter");
|
||||
});
|
||||
|
||||
it("shows cell detail metadata by default and preserves collapsed state", () => {
|
||||
expect(normalizeEditorSettings({}).cellDetailMetadataCollapsed).toBe(false);
|
||||
expect(normalizeEditorSettings({ cellDetailMetadataCollapsed: true }).cellDetailMetadataCollapsed).toBe(true);
|
||||
});
|
||||
|
||||
it("normalizes toolbar item settings from older saved settings", () => {
|
||||
const settings = normalizeEditorSettings({
|
||||
toolbarItems: {
|
||||
|
|
|
|||
|
|
@ -404,6 +404,7 @@ export interface EditorSettings {
|
|||
cellDetailDrawerWidth: number;
|
||||
cellDetailPanelLayout: CellDetailPanelLayout;
|
||||
cellDetailJsonFormatted: boolean;
|
||||
cellDetailMetadataCollapsed: boolean;
|
||||
shortcuts: ShortcutSettings;
|
||||
sqlFormatter: SqlFormatterSettings;
|
||||
sidebarActivation: SidebarActivation;
|
||||
|
|
@ -540,6 +541,7 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
|
|||
cellDetailDrawerWidth: 380,
|
||||
cellDetailPanelLayout: "bottom",
|
||||
cellDetailJsonFormatted: false,
|
||||
cellDetailMetadataCollapsed: false,
|
||||
shortcuts: normalizeShortcutSettings(),
|
||||
sqlFormatter: normalizeSqlFormatterSettings(DEFAULT_SQL_FORMATTER_SETTINGS),
|
||||
sidebarActivation: "single",
|
||||
|
|
@ -776,6 +778,7 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
|
|||
cellDetailDrawerWidth: normalizeDrawerWidth(settings.cellDetailDrawerWidth, 260, DEFAULT_EDITOR_SETTINGS.cellDetailDrawerWidth),
|
||||
cellDetailPanelLayout: normalizeCellDetailPanelLayout(settings.cellDetailPanelLayout),
|
||||
cellDetailJsonFormatted: typeof settings.cellDetailJsonFormatted === "boolean" ? settings.cellDetailJsonFormatted : DEFAULT_EDITOR_SETTINGS.cellDetailJsonFormatted,
|
||||
cellDetailMetadataCollapsed: typeof settings.cellDetailMetadataCollapsed === "boolean" ? settings.cellDetailMetadataCollapsed : DEFAULT_EDITOR_SETTINGS.cellDetailMetadataCollapsed,
|
||||
shortcuts: normalizeShortcutSettings(settings.shortcuts),
|
||||
sqlFormatter: normalizeSqlFormatterSettings(settings.sqlFormatter),
|
||||
sidebarActivation: settings.sidebarActivation === "single" || settings.sidebarActivation === "double" ? settings.sidebarActivation : DEFAULT_EDITOR_SETTINGS.sidebarActivation,
|
||||
|
|
@ -1024,6 +1027,7 @@ export const useSettingsStore = defineStore("settings", () => {
|
|||
if (partial.cellDetailDrawerWidth !== undefined) editorSettings.value.cellDetailDrawerWidth = normalizeDrawerWidth(partial.cellDetailDrawerWidth, 260, DEFAULT_EDITOR_SETTINGS.cellDetailDrawerWidth);
|
||||
if (partial.cellDetailPanelLayout !== undefined) editorSettings.value.cellDetailPanelLayout = normalizeCellDetailPanelLayout(partial.cellDetailPanelLayout);
|
||||
if (partial.cellDetailJsonFormatted !== undefined) editorSettings.value.cellDetailJsonFormatted = partial.cellDetailJsonFormatted === true;
|
||||
if (partial.cellDetailMetadataCollapsed !== undefined) editorSettings.value.cellDetailMetadataCollapsed = partial.cellDetailMetadataCollapsed === true;
|
||||
if (partial.shortcuts !== undefined) editorSettings.value.shortcuts = normalizeShortcutSettings(partial.shortcuts);
|
||||
if (partial.sqlFormatter !== undefined) editorSettings.value.sqlFormatter = normalizeSqlFormatterSettings(partial.sqlFormatter);
|
||||
if (partial.sidebarActivation !== undefined) editorSettings.value.sidebarActivation = partial.sidebarActivation;
|
||||
|
|
|
|||
Loading…
Reference in New Issue