feat(data-grid): color cells by database-aware types
This commit is contained in:
25 files changed
+619
-46
No files matched your search
@@ -2,7 +2,7 @@
|
||||
import { computed, ref, nextTick, watch, onMounted, onBeforeUnmount } from "vue";
|
||||
import { uuid } from "@/lib/common/utils";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { RefreshCw, Trash2, Plus, Save, ChevronDown, ChevronLeft, ChevronRight, Table2, Braces, X, Search, Wrench, Filter, Columns3Cog, SquareDashed, Minus, Rows3, AlignLeft, AlignRight, EyeOff } from "@lucide/vue";
|
||||
import { RefreshCw, Trash2, Plus, Save, ChevronDown, ChevronLeft, ChevronRight, Table2, Braces, X, Search, Wrench, Filter, Columns3Cog, SquareDashed, Minus, Rows3, AlignLeft, AlignRight, EyeOff, Palette } from "@lucide/vue";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Input } from "@/components/ui/input";
|
||||
@@ -135,6 +135,7 @@ const columnWidthDensity = computed(() => settingsStore.editorSettings.columnWid
|
||||
const dataGridRenderMode = computed(() => settingsStore.editorSettings.dataGridRenderMode);
|
||||
const tableFontSize = computed(() => settingsStore.editorSettings.tableFontSize);
|
||||
const numericColumnRightAlign = computed(() => settingsStore.editorSettings.numericColumnRightAlign ?? true);
|
||||
const colorizeDataGridCellTypes = computed(() => settingsStore.editorSettings.colorizeDataGridCellTypes);
|
||||
const viewMode = computed<ViewMode>({
|
||||
get: () => settingsStore.editorSettings.mongoViewMode,
|
||||
set: (value) => settingsStore.updateEditorSettings({ mongoViewMode: value }),
|
||||
@@ -177,6 +178,10 @@ function increaseTableFontSize() {
|
||||
function setNumericColumnRightAlign(value: boolean) {
|
||||
settingsStore.updateEditorSettings({ numericColumnRightAlign: value });
|
||||
}
|
||||
|
||||
function setColorizeDataGridCellTypes(value: boolean) {
|
||||
settingsStore.updateEditorSettings({ colorizeDataGridCellTypes: value });
|
||||
}
|
||||
const tableSearchSplitContainerRef = ref<HTMLDivElement>();
|
||||
const tableFindPaneWidth = ref<number | null>(null);
|
||||
const isResizingTableSearchSplit = ref(false);
|
||||
@@ -1867,6 +1872,13 @@ defineExpose({ focusSearch });
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-3 px-3 py-1.5 text-xs">
|
||||
<div class="min-w-0 flex items-center gap-2 font-medium">
|
||||
<Palette class="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
||||
<span>{{ t("grid.colorizeDataTypes") }}</span>
|
||||
</div>
|
||||
<Switch size="sm" :model-value="colorizeDataGridCellTypes" :aria-label="t('grid.colorizeDataTypes')" @update:model-value="setColorizeDataGridCellTypes" />
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-3 px-3 py-1.5 text-xs" :class="{ 'opacity-60': !dataGridRef?.canToggleAllNullColumns }">
|
||||
<span class="min-w-0 flex items-center gap-2 font-medium">
|
||||
<EyeOff class="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
||||
|
||||
@@ -309,6 +309,7 @@ const debugLogCopied = ref(false);
|
||||
const debugLogDownloaded = ref(false);
|
||||
const editShowColumnCommentsInHeader = ref(settingsStore.editorSettings.showColumnCommentsInHeader);
|
||||
const editShowColumnTypesInHeader = ref(settingsStore.editorSettings.showColumnTypesInHeader);
|
||||
const editColorizeDataGridCellTypes = ref(settingsStore.editorSettings.colorizeDataGridCellTypes);
|
||||
const editShowIndexIndicatorsInHeader = ref(settingsStore.editorSettings.showIndexIndicatorsInHeader);
|
||||
const editCompactColumnHeaderActions = ref(settingsStore.editorSettings.compactColumnHeaderActions);
|
||||
const editDataGridQuickEntry = ref(settingsStore.editorSettings.dataGridQuickEntry);
|
||||
@@ -482,6 +483,7 @@ function currentEditorSettingsDraft(): EditorSettingsDraft {
|
||||
tabLayout: editTabLayout.value,
|
||||
showColumnCommentsInHeader: editShowColumnCommentsInHeader.value,
|
||||
showColumnTypesInHeader: editShowColumnTypesInHeader.value,
|
||||
colorizeDataGridCellTypes: editColorizeDataGridCellTypes.value,
|
||||
showIndexIndicatorsInHeader: editShowIndexIndicatorsInHeader.value,
|
||||
compactColumnHeaderActions: editCompactColumnHeaderActions.value,
|
||||
dataGridQuickEntry: editDataGridQuickEntry.value,
|
||||
@@ -754,6 +756,7 @@ function syncEditorSettingsDraftFromStore() {
|
||||
editTabLayout.value = settingsStore.editorSettings.tabLayout;
|
||||
editShowColumnCommentsInHeader.value = settingsStore.editorSettings.showColumnCommentsInHeader;
|
||||
editShowColumnTypesInHeader.value = settingsStore.editorSettings.showColumnTypesInHeader;
|
||||
editColorizeDataGridCellTypes.value = settingsStore.editorSettings.colorizeDataGridCellTypes;
|
||||
editShowIndexIndicatorsInHeader.value = settingsStore.editorSettings.showIndexIndicatorsInHeader;
|
||||
editCompactColumnHeaderActions.value = settingsStore.editorSettings.compactColumnHeaderActions;
|
||||
editDataGridQuickEntry.value = settingsStore.editorSettings.dataGridQuickEntry;
|
||||
@@ -998,6 +1001,7 @@ function resetDefaultsForTab(tab: SettingsCategory) {
|
||||
} else if (tab === "data") {
|
||||
editShowColumnCommentsInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnCommentsInHeader;
|
||||
editShowColumnTypesInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnTypesInHeader;
|
||||
editColorizeDataGridCellTypes.value = DEFAULT_EDITOR_SETTINGS.colorizeDataGridCellTypes;
|
||||
editShowIndexIndicatorsInHeader.value = DEFAULT_EDITOR_SETTINGS.showIndexIndicatorsInHeader;
|
||||
editCompactColumnHeaderActions.value = DEFAULT_EDITOR_SETTINGS.compactColumnHeaderActions;
|
||||
editDataGridQuickEntry.value = DEFAULT_EDITOR_SETTINGS.dataGridQuickEntry;
|
||||
@@ -1065,6 +1069,7 @@ function resetAllDefaults() {
|
||||
editSidebarTablePageSize.value = DEFAULT_SIDEBAR_TABLE_PAGE_SIZE;
|
||||
editShowColumnCommentsInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnCommentsInHeader;
|
||||
editShowColumnTypesInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnTypesInHeader;
|
||||
editColorizeDataGridCellTypes.value = DEFAULT_EDITOR_SETTINGS.colorizeDataGridCellTypes;
|
||||
editShowIndexIndicatorsInHeader.value = DEFAULT_EDITOR_SETTINGS.showIndexIndicatorsInHeader;
|
||||
editCompactColumnHeaderActions.value = DEFAULT_EDITOR_SETTINGS.compactColumnHeaderActions;
|
||||
editDataGridQuickEntry.value = DEFAULT_EDITOR_SETTINGS.dataGridQuickEntry;
|
||||
@@ -4817,6 +4822,17 @@ onUnmounted(() => {
|
||||
</div>
|
||||
<Switch id="show-column-types-in-header" v-model="editShowColumnTypesInHeader" />
|
||||
</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="colorize-data-grid-cell-types">
|
||||
{{ t("settings.colorizeDataGridCellTypes") }}
|
||||
</Label>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ t("settings.colorizeDataGridCellTypesDescription") }}
|
||||
</p>
|
||||
</div>
|
||||
<Switch id="colorize-data-grid-cell-types" v-model="editColorizeDataGridCellTypes" />
|
||||
</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="show-index-indicators-in-header">
|
||||
|
||||
@@ -83,7 +83,8 @@ import { buildTableSelectSql, quoteTableDataIdentifier } from "@/lib/table/table
|
||||
import { tableOpenPageLimit } from "@/lib/table/tableOpenPageLimit";
|
||||
import { uuid } from "@/lib/common/utils";
|
||||
import { generateCellValues, type CellValueGenerationKind } from "@/lib/dataGrid/cellValueGeneration";
|
||||
import { compactHeaderColumnType, isNumericColumnType, resolveHeaderColumnType, resolveResultColumnType } from "@/lib/dataGrid/dataGridColumnType";
|
||||
import { compactHeaderColumnType, isNumericColumnType, resolveDataGridTypeVisualKind, resolveHeaderColumnType, resolveResultColumnType } from "@/lib/dataGrid/dataGridColumnType";
|
||||
import { dataGridCellTextClass, dataGridTypeVisualClass } from "@/lib/dataGrid/dataGridCellTextVisual";
|
||||
import {
|
||||
canDeleteExistingTdengineRows,
|
||||
canEditExistingTableRows,
|
||||
@@ -292,6 +293,7 @@ const multiRowTranspose = computed(() => settingsStore.editorSettings.dataGridMu
|
||||
const hideNullColumns = computed(() => settingsStore.editorSettings.dataGridHideNullColumns);
|
||||
const booleanCellsUseCheckbox = computed(() => settingsStore.editorSettings.dataGridBooleanDisplayMode === "checkbox");
|
||||
const booleanDisplayMode = computed(() => settingsStore.editorSettings.dataGridBooleanDisplayMode);
|
||||
const colorizeDataGridCellTypes = computed(() => settingsStore.editorSettings.colorizeDataGridCellTypes);
|
||||
const { isDark, themePalette } = useTheme();
|
||||
const { toast } = useToast();
|
||||
// 外键单元格跳转复用导航入口;对话框引用传 stub(同 AiAssistant 模式)
|
||||
@@ -657,23 +659,7 @@ function selectedSortMenuValue(column: string, columnIndex: number): SortMenuVal
|
||||
}
|
||||
|
||||
function typeColorClass(t: string): string {
|
||||
// Strip precision/scale suffix like (20,6)
|
||||
const base = t.replace(/\(.*\)$/, "").toLowerCase();
|
||||
if (["int", "int2", "int4", "int8", "smallint", "bigint", "integer", "serial", "bigserial", "tinyint", "mediumint"].includes(base)) return "text-blue-500";
|
||||
// number: Oracle/Dameng NUMBER;binary_float/binary_double: Oracle IEEE 浮点
|
||||
if (["float4", "float8", "double", "decimal", "numeric", "real", "float", "money", "number", "binary_float", "binary_double", "dec"].includes(base)) return "text-cyan-500";
|
||||
// varchar2/nvarchar2/long: Oracle/Dameng 字符类型(LONG 在 Oracle 中是变长字符)
|
||||
if (["varchar", "varchar2", "nvarchar2", "text", "char", "character varying", "character", "string", "nvarchar", "nchar", "ntext", "longtext", "mediumtext", "tinytext", "clob", "long"].includes(base)) return "text-green-500";
|
||||
if (["bool", "boolean", "bit"].includes(base)) return "text-orange-500";
|
||||
if (["timestamp", "timestamptz", "datetime", "date", "time", "timetz", "datetime2", "smalldatetime"].includes(base)) return "text-purple-500";
|
||||
// xmltype: Oracle XMLType
|
||||
if (["json", "jsonb", "xml", "xmltype", "array"].includes(base)) return "text-pink-500";
|
||||
if (["uuid", "uniqueidentifier"].includes(base)) return "text-amber-500";
|
||||
// raw/long raw/bfile: Oracle 二进制
|
||||
if (["bytea", "blob", "binary", "varbinary", "image", "raw", "long raw", "bfile"].includes(base)) return "text-red-400";
|
||||
// sdo_geometry: Oracle Spatial
|
||||
if (["geometry", "geography", "sdo_geometry"].includes(base)) return "text-emerald-500";
|
||||
return "text-muted-foreground";
|
||||
return dataGridTypeVisualClass(resolveDataGridTypeVisualKind(t, resolvedDatabaseType.value));
|
||||
}
|
||||
const contextCell = ref<{
|
||||
rowId: number;
|
||||
@@ -1976,6 +1962,8 @@ const allColumnTypes = computed(() =>
|
||||
),
|
||||
);
|
||||
const visibleColumnTypes = computed(() => visibleColumnIndexes.value.map((index) => allColumnTypes.value[index]));
|
||||
const allColumnTypeVisualKinds = computed(() => allColumnTypes.value.map((type) => resolveDataGridTypeVisualKind(type, resolvedDatabaseType.value)));
|
||||
const visibleColumnTypeVisualKinds = computed(() => visibleColumnIndexes.value.map((index) => allColumnTypeVisualKinds.value[index] ?? "unknown"));
|
||||
const visibleColumnComments = computed(() => visibleColumnIndexes.value.map((index) => dataGridColumnCommentFor(columnCommentMap.value, props.result.columns[index] ?? "", props.sourceColumns?.[index])));
|
||||
const visibleColumnCount = computed(() => visibleColumnIndexes.value.length);
|
||||
|
||||
@@ -1985,6 +1973,45 @@ const columnAligns = computed<("left" | "right")[]>(() => {
|
||||
return visibleColumnTypes.value.map((type) => (isNumericColumnType(type) ? "right" : "left"));
|
||||
});
|
||||
|
||||
function gridCellTextColorClass(item: RowItem, actualColIdx: number, visibleColIdx: number): string {
|
||||
if (!colorizeDataGridCellTypes.value) return "text-foreground";
|
||||
const value = item.data[actualColIdx];
|
||||
const checkbox = booleanCellsUseCheckbox.value && isBooleanGridCell(item, actualColIdx) && value !== null;
|
||||
return dataGridCellTextClass({
|
||||
colorizeTypes: colorizeDataGridCellTypes.value,
|
||||
typeKind: allColumnTypeVisualKinds.value[actualColIdx] ?? "unknown",
|
||||
isNull: value === null,
|
||||
isDraft: item.isDraft && value === null,
|
||||
isEditing: editingCell.value?.rowId === item.id && editingCell.value.col === actualColIdx,
|
||||
isControl: checkbox,
|
||||
isSelected: rowCellsUseSelectionVisual(item.id) || cellIsSelected(item.displayIndex, visibleColIdx),
|
||||
isCurrentSearchMatch: cellIsCurrentMatch(item.displayIndex, actualColIdx),
|
||||
isSearchMatch: cellIsSearchMatch(item.displayIndex, actualColIdx),
|
||||
isDirty: item.isDirtyCol[actualColIdx],
|
||||
isDeleted: item.isDeleted,
|
||||
});
|
||||
}
|
||||
|
||||
function transposeCellTextColorClass(recordIndex: number, actualColIdx: number): string {
|
||||
if (!colorizeDataGridCellTypes.value) return "text-foreground";
|
||||
const item = displayItems.value[recordIndex];
|
||||
if (!item) return "text-foreground";
|
||||
const value = item.data[actualColIdx];
|
||||
return dataGridCellTextClass({
|
||||
colorizeTypes: colorizeDataGridCellTypes.value,
|
||||
typeKind: allColumnTypeVisualKinds.value[actualColIdx] ?? "unknown",
|
||||
isNull: value === null,
|
||||
isDraft: item.isDraft && value === null,
|
||||
isEditing: editingCell.value?.rowId === item.id && editingCell.value.col === actualColIdx,
|
||||
isControl: booleanCellsUseCheckbox.value && isBooleanGridCell(item, actualColIdx) && value !== null,
|
||||
isSelected: transposeRecordUsesSelectionVisual(recordIndex) || transposeCellIsSelected(recordIndex, actualColIdx),
|
||||
isCurrentSearchMatch: cellIsCurrentMatch(recordIndex, actualColIdx),
|
||||
isSearchMatch: cellIsSearchMatch(recordIndex, actualColIdx),
|
||||
isDirty: item.isDirtyCol[actualColIdx],
|
||||
isDeleted: item.isDeleted,
|
||||
});
|
||||
}
|
||||
|
||||
/** Preview actions from the result preview registry for the current result. */
|
||||
const previewActions = computed(() => {
|
||||
if (!props.result) return [];
|
||||
@@ -5349,10 +5376,6 @@ function queryColumnRef(name: string): string {
|
||||
return props.databaseType === "neo4j" ? `n.${quoted}` : quoted;
|
||||
}
|
||||
|
||||
function isNull(value: unknown): boolean {
|
||||
return value === null;
|
||||
}
|
||||
|
||||
function rowNumberStatusClass(item: RowItem): string {
|
||||
if (item.status === "draft") {
|
||||
return "border-muted-foreground/20 bg-muted/20 font-semibold text-muted-foreground";
|
||||
@@ -6077,6 +6100,8 @@ function drawCanvasGrid() {
|
||||
pageOffset: rowNumberPageOffset(),
|
||||
frozenColumnCount: frozenColumnCount.value,
|
||||
columnAligns: columnAligns.value,
|
||||
columnTypeVisualKinds: visibleColumnTypeVisualKinds.value,
|
||||
colorizeDataTypes: colorizeDataGridCellTypes.value,
|
||||
rightAlignedActionCell: canvasRightAlignedActionCell.value,
|
||||
columnIsBoolean: isBooleanGridColumn,
|
||||
booleanDisplayMode: booleanDisplayMode.value,
|
||||
@@ -6097,6 +6122,7 @@ watch(showDataGridTopbar, () => nextTick(observeDataGridTopbarWidth), {
|
||||
});
|
||||
watch(columnAligns, () => scheduleCanvasDraw());
|
||||
watch(booleanDisplayMode, () => scheduleCanvasDraw());
|
||||
watch(colorizeDataGridCellTypes, () => scheduleCanvasDraw());
|
||||
watch(
|
||||
[
|
||||
displayRowRefs,
|
||||
@@ -10163,22 +10189,24 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
|
||||
v-for="cell in item.values"
|
||||
:key="`${item.id}:${cell.recordIndex}`"
|
||||
class="relative flex shrink-0 items-center border-r border-border/70 px-2 py-0 truncate"
|
||||
:class="{
|
||||
'text-muted-foreground italic': cell.isNull,
|
||||
'cell-selected': transposeCellIsSelected(cell.recordIndex, cell.valueIndex) && !displayItems[cell.recordIndex]?.isDirtyCol[cell.valueIndex],
|
||||
'cell-selected-dirty': transposeCellIsSelected(cell.recordIndex, cell.valueIndex) && displayItems[cell.recordIndex]?.isDirtyCol[cell.valueIndex],
|
||||
'cell-selected--sparse': transposeCellIsSelected(cell.recordIndex, cell.valueIndex) && !displayItems[cell.recordIndex]?.isDirtyCol[cell.valueIndex],
|
||||
'cell-selected-dirty--sparse': transposeCellIsSelected(cell.recordIndex, cell.valueIndex) && displayItems[cell.recordIndex]?.isDirtyCol[cell.valueIndex],
|
||||
'row-cell-selected': transposeRecordUsesSelectionVisual(cell.recordIndex) && !transposeCellIsSelected(cell.recordIndex, cell.valueIndex) && !displayItems[cell.recordIndex]?.isDirtyCol[cell.valueIndex],
|
||||
'row-cell-selected-dirty': transposeRecordUsesSelectionVisual(cell.recordIndex) && !transposeCellIsSelected(cell.recordIndex, cell.valueIndex) && displayItems[cell.recordIndex]?.isDirtyCol[cell.valueIndex],
|
||||
'bg-primary/15': transposeRecordUsesActiveHighlight(cell.recordIndex) && !transposeRecordUsesSelectionVisual(cell.recordIndex) && !displayItems[cell.recordIndex]?.isDirtyCol[cell.valueIndex] && !transposeCellIsSelected(cell.recordIndex, cell.valueIndex),
|
||||
'bg-yellow-500/10 cell-dirty': displayItems[cell.recordIndex]?.isDirtyCol[cell.valueIndex],
|
||||
'bg-yellow-200/60 dark:bg-yellow-500/20': cellIsSearchMatch(cell.recordIndex, cell.valueIndex),
|
||||
'ring-2 ring-inset ring-yellow-500 bg-yellow-300/60 dark:bg-yellow-500/40': cellIsCurrentMatch(cell.recordIndex, cell.valueIndex),
|
||||
'cursor-text': !isScrolling && canEditCellItem(displayItems[cell.recordIndex], cell.valueIndex),
|
||||
'hover:bg-gray-200 dark:hover:bg-gray-800':
|
||||
!isScrolling && canEditCellItem(displayItems[cell.recordIndex], cell.valueIndex) && !transposeRecordUsesSelectionVisual(cell.recordIndex) && !transposeRecordUsesActiveHighlight(cell.recordIndex) && !transposeCellIsSelected(cell.recordIndex, cell.valueIndex),
|
||||
}"
|
||||
:class="[
|
||||
transposeCellTextColorClass(cell.recordIndex, cell.valueIndex),
|
||||
{
|
||||
'cell-selected': transposeCellIsSelected(cell.recordIndex, cell.valueIndex) && !displayItems[cell.recordIndex]?.isDirtyCol[cell.valueIndex],
|
||||
'cell-selected-dirty': transposeCellIsSelected(cell.recordIndex, cell.valueIndex) && displayItems[cell.recordIndex]?.isDirtyCol[cell.valueIndex],
|
||||
'cell-selected--sparse': transposeCellIsSelected(cell.recordIndex, cell.valueIndex) && !displayItems[cell.recordIndex]?.isDirtyCol[cell.valueIndex],
|
||||
'cell-selected-dirty--sparse': transposeCellIsSelected(cell.recordIndex, cell.valueIndex) && displayItems[cell.recordIndex]?.isDirtyCol[cell.valueIndex],
|
||||
'row-cell-selected': transposeRecordUsesSelectionVisual(cell.recordIndex) && !transposeCellIsSelected(cell.recordIndex, cell.valueIndex) && !displayItems[cell.recordIndex]?.isDirtyCol[cell.valueIndex],
|
||||
'row-cell-selected-dirty': transposeRecordUsesSelectionVisual(cell.recordIndex) && !transposeCellIsSelected(cell.recordIndex, cell.valueIndex) && displayItems[cell.recordIndex]?.isDirtyCol[cell.valueIndex],
|
||||
'bg-primary/15': transposeRecordUsesActiveHighlight(cell.recordIndex) && !transposeRecordUsesSelectionVisual(cell.recordIndex) && !displayItems[cell.recordIndex]?.isDirtyCol[cell.valueIndex] && !transposeCellIsSelected(cell.recordIndex, cell.valueIndex),
|
||||
'bg-yellow-500/10 cell-dirty': displayItems[cell.recordIndex]?.isDirtyCol[cell.valueIndex],
|
||||
'bg-yellow-200/60 dark:bg-yellow-500/20': cellIsSearchMatch(cell.recordIndex, cell.valueIndex),
|
||||
'ring-2 ring-inset ring-yellow-500 bg-yellow-300/60 dark:bg-yellow-500/40': cellIsCurrentMatch(cell.recordIndex, cell.valueIndex),
|
||||
'cursor-text': !isScrolling && canEditCellItem(displayItems[cell.recordIndex], cell.valueIndex),
|
||||
'hover:bg-gray-200 hover:text-foreground dark:hover:bg-gray-800':
|
||||
!isScrolling && canEditCellItem(displayItems[cell.recordIndex], cell.valueIndex) && !transposeRecordUsesSelectionVisual(cell.recordIndex) && !transposeRecordUsesActiveHighlight(cell.recordIndex) && !transposeCellIsSelected(cell.recordIndex, cell.valueIndex),
|
||||
},
|
||||
]"
|
||||
:style="{
|
||||
width: `${getTransposeRecordWidth(cell.recordIndex)}px`,
|
||||
}"
|
||||
@@ -10915,12 +10943,12 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
|
||||
class="data-grid-cell group/cell shrink-0 px-3 py-1 border-r border-border whitespace-nowrap overflow-hidden text-ellipsis relative select-none inline-block items-center tabular-nums"
|
||||
:style="renderedColumnStyle(col.visibleColIdx)"
|
||||
:class="[
|
||||
gridCellTextColorClass(item, col.actualColIdx, col.visibleColIdx),
|
||||
selectionFrameEdgeClass(item.displayIndex, col.visibleColIdx),
|
||||
{
|
||||
'data-grid-cell--frozen': col.visibleColIdx < frozenColumnCount,
|
||||
'data-grid-cell--frozen-separator': frozenColumnCount > 0 && col.visibleColIdx === frozenColumnCount - 1,
|
||||
'text-right': columnAligns[col.visibleColIdx] === 'right',
|
||||
'text-muted-foreground italic': isNull(item.data[col.actualColIdx]),
|
||||
'bg-yellow-500/10 cell-dirty': item.isDirtyCol[col.actualColIdx],
|
||||
'cell-selected': cellIsSelected(item.displayIndex, col.visibleColIdx) && !item.isDirtyCol[col.actualColIdx],
|
||||
'cell-selected-dirty': cellIsSelected(item.displayIndex, col.visibleColIdx) && item.isDirtyCol[col.actualColIdx],
|
||||
@@ -10934,7 +10962,7 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
|
||||
'bg-yellow-200/60 dark:bg-yellow-500/20': cellIsSearchMatch(item.displayIndex, col.actualColIdx),
|
||||
'ring-2 ring-inset ring-yellow-500 bg-yellow-300/60 dark:bg-yellow-500/40': cellIsCurrentMatch(item.displayIndex, col.actualColIdx),
|
||||
'tabular-nums': typeof item.data[col.actualColIdx] === 'number',
|
||||
'cursor-text hover:bg-gray-200 dark:hover:bg-gray-800': !isScrolling && canEditCellItem(item, col.actualColIdx) && !(booleanCellsUseCheckbox && isBooleanGridCell(item, col.actualColIdx) && item.data[col.actualColIdx] !== null),
|
||||
'cursor-text hover:bg-gray-200 hover:text-foreground dark:hover:bg-gray-800': !isScrolling && canEditCellItem(item, col.actualColIdx) && !(booleanCellsUseCheckbox && isBooleanGridCell(item, col.actualColIdx) && item.data[col.actualColIdx] !== null),
|
||||
'cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-800': !isScrolling && booleanCellsUseCheckbox && isBooleanGridCell(item, col.actualColIdx) && item.data[col.actualColIdx] !== null && canEditCellItem(item, col.actualColIdx),
|
||||
'line-through': item.isDeleted,
|
||||
'overflow-visible z-20 border-r-transparent': editingCell?.rowId === item.id && editingCell?.col === col.actualColIdx,
|
||||
|
||||
@@ -7,7 +7,36 @@ import { defaultViewForResult } from "@/lib/query/queryResultDefaultView";
|
||||
import { isQueryExecutionErrorResult } from "@/lib/query/queryResultError";
|
||||
import type { CSSProperties } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Check, CheckSquare2, Columns3Cog, Copy, EyeOff, Loader2, Search, TableProperties, ChevronDown, ChevronUp, Inbox, RefreshCcw, Wrench, Toolbox, Database, Download, Upload, X, Pin, Rows3, SquareDashed, Minus, Plus, ShieldAlert, AlignLeft, AlignRight, PanelsTopLeft } from "@lucide/vue";
|
||||
import {
|
||||
Check,
|
||||
CheckSquare2,
|
||||
Columns3Cog,
|
||||
Copy,
|
||||
EyeOff,
|
||||
Loader2,
|
||||
Search,
|
||||
TableProperties,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
Inbox,
|
||||
RefreshCcw,
|
||||
Wrench,
|
||||
Toolbox,
|
||||
Database,
|
||||
Download,
|
||||
Upload,
|
||||
X,
|
||||
Pin,
|
||||
Rows3,
|
||||
SquareDashed,
|
||||
Minus,
|
||||
Plus,
|
||||
ShieldAlert,
|
||||
AlignLeft,
|
||||
AlignRight,
|
||||
PanelsTopLeft,
|
||||
Palette,
|
||||
} from "@lucide/vue";
|
||||
import { Splitpanes, Pane } from "splitpanes";
|
||||
import { DynamicScroller, DynamicScrollerItem } from "vue-virtual-scroller";
|
||||
import "splitpanes/dist/splitpanes.css";
|
||||
@@ -194,6 +223,8 @@ const connectionStore = useConnectionStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const booleanDisplayMode = computed(() => settingsStore.editorSettings.dataGridBooleanDisplayMode);
|
||||
const setBooleanDisplayMode = (mode: "checkbox" | "dropdown") => settingsStore.updateEditorSettings({ dataGridBooleanDisplayMode: mode });
|
||||
const colorizeDataGridCellTypes = computed(() => settingsStore.editorSettings.colorizeDataGridCellTypes);
|
||||
const setColorizeDataGridCellTypes = (value: boolean) => settingsStore.updateEditorSettings({ colorizeDataGridCellTypes: value });
|
||||
const { toast } = useToast();
|
||||
const DEFAULT_QUERY_RESULTS_PANE_SIZE = 68;
|
||||
|
||||
@@ -1365,6 +1396,13 @@ defineExpose({ focusSearch, refreshData, refreshQueryEditorCompletionCache, hand
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-3 px-3 py-1.5 text-xs">
|
||||
<div class="min-w-0 flex items-center gap-2 font-medium">
|
||||
<Palette class="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
||||
<span>{{ t("grid.colorizeDataTypes") }}</span>
|
||||
</div>
|
||||
<Switch size="sm" :model-value="colorizeDataGridCellTypes" :aria-label="t('grid.colorizeDataTypes')" @update:model-value="setColorizeDataGridCellTypes" />
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-3 px-3 py-1.5 text-xs" :class="{ 'opacity-60': !dataGridRef?.canToggleAllNullColumns }">
|
||||
<span class="min-w-0 flex items-center gap-2 font-medium">
|
||||
<EyeOff class="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
||||
@@ -1858,6 +1896,13 @@ defineExpose({ focusSearch, refreshData, refreshQueryEditorCompletionCache, hand
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-3 px-3 py-1.5 text-xs">
|
||||
<div class="min-w-0 flex items-center gap-2 font-medium">
|
||||
<Palette class="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
||||
<span>{{ t("grid.colorizeDataTypes") }}</span>
|
||||
</div>
|
||||
<Switch size="sm" :model-value="colorizeDataGridCellTypes" :aria-label="t('grid.colorizeDataTypes')" @update:model-value="setColorizeDataGridCellTypes" />
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-3 px-3 py-1.5 text-xs" :class="{ 'opacity-60': !dataGridRef?.canToggleAllNullColumns }">
|
||||
<span class="min-w-0 flex items-center gap-2 font-medium">
|
||||
<EyeOff class="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
||||
|
||||
Reference in new issue
Block a user