mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-02 02:34:42 +08:00
fix(mongodb): null cell display and inline editing
This commit is contained in:
@@ -99,7 +99,7 @@ import type { BuildSingleColumnAlterSqlOptions } from "@/lib/table/tableStructur
|
||||
import { buildTableSelectSql, qualifyTableReferencesInSql, quoteTableDataIdentifier } from "@/lib/table/tableSelectSql";
|
||||
import { uuid } from "@/lib/common/utils";
|
||||
import { generateCellValues, type CellValueGenerationKind } from "@/lib/dataGrid/cellValueGeneration";
|
||||
import { MONGO_DOCUMENT_GRID_NULL, mongoDocumentGridDisplayText, mongoDocumentGridEditorText, mongoDocumentGridExternalValue, mongoDocumentGridInputValue } from "@/lib/mongo/mongoDocumentValues";
|
||||
import { MONGO_DOCUMENT_GRID_NULL, mongoDocumentGridClipboardText, mongoDocumentGridDisplayText, mongoDocumentGridEditorText, mongoDocumentGridExternalValue, mongoDocumentGridInputValue } from "@/lib/mongo/mongoDocumentValues";
|
||||
import { compactHeaderColumnType, formatMetadataColumnTypeLabel, isNumericColumnType, resolveDataGridTypeVisualKind, resolveHeaderColumnType, resolveResultColumnType } from "@/lib/dataGrid/dataGridColumnType";
|
||||
import { dataGridCellTextClass, dataGridTypeVisualClass } from "@/lib/dataGrid/dataGridCellTextVisual";
|
||||
import { DATA_GRID_TYPE_COLOR_KEYS, resolveActiveDataGridTypeColors } from "@/lib/dataGrid/dataGridTypeColorScheme";
|
||||
@@ -2017,8 +2017,10 @@ const columnAligns = computed<("left" | "right")[]>(() => {
|
||||
});
|
||||
|
||||
function gridCellTextColorClass(item: RowItem, actualColIdx: number, visibleColIdx: number): string {
|
||||
if (!colorizeDataGridCellTypes.value) return "text-foreground";
|
||||
const value = item.data[actualColIdx];
|
||||
const isMongoDocumentNull = props.mongoCollectionGrid === true && value === MONGO_DOCUMENT_GRID_NULL;
|
||||
if (isMongoDocumentNull) return "text-muted-foreground italic";
|
||||
if (!colorizeDataGridCellTypes.value) return "text-foreground";
|
||||
const checkbox = booleanCellsUseCheckbox.value && isBooleanGridCell(item, actualColIdx) && value !== null;
|
||||
return dataGridCellTextClass({
|
||||
colorizeTypes: colorizeDataGridCellTypes.value,
|
||||
@@ -2036,10 +2038,12 @@ function gridCellTextColorClass(item: RowItem, actualColIdx: number, visibleColI
|
||||
}
|
||||
|
||||
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];
|
||||
const isMongoDocumentNull = props.mongoCollectionGrid === true && value === MONGO_DOCUMENT_GRID_NULL;
|
||||
if (isMongoDocumentNull) return "text-muted-foreground italic";
|
||||
if (!colorizeDataGridCellTypes.value) return "text-foreground";
|
||||
return dataGridCellTextClass({
|
||||
colorizeTypes: colorizeDataGridCellTypes.value,
|
||||
typeKind: allColumnTypeVisualKinds.value[actualColIdx] ?? "unknown",
|
||||
@@ -3947,6 +3951,10 @@ function isIoTDBTimestampColumn(columnIndex: number): boolean {
|
||||
}
|
||||
|
||||
function inlineCellEditorText(value: CellValue, columnIndex: number): string {
|
||||
if (props.mongoCollectionGrid) {
|
||||
const documentGridText = mongoDocumentGridEditorText(value);
|
||||
if (documentGridText !== undefined) return documentGridText;
|
||||
}
|
||||
const columnInfo = tableColumnForGridColumn(columnIndex) ?? resultColumnInfoForGridColumn(columnIndex);
|
||||
const columnType = props.result.column_types?.[columnIndex] ?? columnInfo?.data_type;
|
||||
return (
|
||||
@@ -7073,6 +7081,7 @@ function drawCanvasGrid() {
|
||||
searchMatchKeys: searchMatchSet.value,
|
||||
currentSearchMatch: currentSearchMatch.value,
|
||||
formatCell: (value, columnIndex, row) => formatCellCached(visibleLargeValuePreviewValue(row, columnIndex, value), columnIndex, largeValueOriginalBytes(row, columnIndex)),
|
||||
isNullValue: (value) => value === null || (props.mongoCollectionGrid === true && value === MONGO_DOCUMENT_GRID_NULL),
|
||||
newRowCellPlaceholder,
|
||||
isRowActive,
|
||||
rowCellsUseSelectionVisual,
|
||||
@@ -7395,7 +7404,7 @@ const {
|
||||
columnComments: visibleColumnComments,
|
||||
allColumnComments,
|
||||
displayValue: formatCellCached,
|
||||
cellClipboardText: (value) => (props.mongoCollectionGrid ? mongoDocumentGridEditorText(value) : undefined),
|
||||
cellClipboardText: (value) => (props.mongoCollectionGrid ? mongoDocumentGridClipboardText(value) : undefined),
|
||||
externalCellValue: (value) => (props.mongoCollectionGrid ? mongoDocumentGridExternalValue(value) : value),
|
||||
mongoDocuments: computed(() => props.result.mongo_copy_documents ?? props.result.mongo_documents),
|
||||
spatialColumns: computed(() => props.result.spatial_columns),
|
||||
@@ -8933,7 +8942,7 @@ async function onGridKeydown(event: KeyboardEvent) {
|
||||
|
||||
function detailClipboardText(detail: DataGridCellDetail): string {
|
||||
if (props.mongoCollectionGrid) {
|
||||
const documentGridText = mongoDocumentGridEditorText(detail.value);
|
||||
const documentGridText = mongoDocumentGridClipboardText(detail.value);
|
||||
if (documentGridText !== undefined) return documentGridText;
|
||||
}
|
||||
if (detail.value === null) return "";
|
||||
|
||||
@@ -103,9 +103,10 @@ describe("data grid cell text visual priority", () => {
|
||||
expect(dataGridSource).toContain("'cursor-text hover:bg-gray-200 hover:text-foreground dark:hover:bg-gray-800':");
|
||||
});
|
||||
|
||||
it("returns before reading cell state when type colors are disabled", () => {
|
||||
expect(dataGridSource).toContain('function gridCellTextColorClass(item: RowItem, actualColIdx: number, visibleColIdx: number): string {\n if (!colorizeDataGridCellTypes.value) return "text-foreground";\n const value = item.data[actualColIdx];');
|
||||
expect(dataGridSource).toContain('function transposeCellTextColorClass(recordIndex: number, actualColIdx: number): string {\n if (!colorizeDataGridCellTypes.value) return "text-foreground";\n const item = displayItems.value[recordIndex];');
|
||||
it("keeps Mongo BSON null muted when type colors are disabled", () => {
|
||||
const mongoNullPriority = 'const isMongoDocumentNull = props.mongoCollectionGrid === true && value === MONGO_DOCUMENT_GRID_NULL;\n if (isMongoDocumentNull) return "text-muted-foreground italic";\n if (!colorizeDataGridCellTypes.value) return "text-foreground";';
|
||||
|
||||
expect(dataGridSource.split(mongoNullPriority)).toHaveLength(3);
|
||||
});
|
||||
|
||||
it("places data-grid type selectors in the components layer", () => {
|
||||
|
||||
@@ -80,6 +80,7 @@ export interface DrawCanvasDataGridOptions {
|
||||
searchMatchKeys: ReadonlySet<number>;
|
||||
currentSearchMatch: CanvasSearchMatch | null;
|
||||
formatCell: (value: CellValue, columnIndex: number, row: CanvasDataGridRow) => string;
|
||||
isNullValue?: (value: CellValue) => boolean;
|
||||
columnIsBoolean?: (columnIndex: number) => boolean;
|
||||
newRowCellPlaceholder?: (row: CanvasDataGridRow, columnIndex: number) => string | null;
|
||||
isRowActive: (rowIndex: number) => boolean;
|
||||
@@ -345,6 +346,7 @@ export function drawCanvasDataGrid(options: DrawCanvasDataGridOptions) {
|
||||
searchMatchKeys,
|
||||
currentSearchMatch,
|
||||
formatCell,
|
||||
isNullValue,
|
||||
newRowCellPlaceholder,
|
||||
isRowActive,
|
||||
rowCellsUseSelectionVisual,
|
||||
@@ -558,6 +560,7 @@ export function drawCanvasDataGrid(options: DrawCanvasDataGridOptions) {
|
||||
ctx.rect(clippedX, y, Math.min(cellPaintWidth, width - clippedX), CANVAS_DATA_GRID_ROW_HEIGHT);
|
||||
ctx.clip();
|
||||
const value = item.data[actualColIdx];
|
||||
const isNullCell = isNullValue?.(value) ?? value === null;
|
||||
const isBooleanCell = columnIsBoolean?.(actualColIdx) === true && isBooleanCellValue(value);
|
||||
const isRightAlign = columnAligns?.[visibleColIdx] === "right";
|
||||
const isEditingThisCell = editingCell?.rowId === item.id && editingCell.col === actualColIdx;
|
||||
@@ -567,7 +570,7 @@ export function drawCanvasDataGrid(options: DrawCanvasDataGridOptions) {
|
||||
const textRole = resolveDataGridCellTextRole({
|
||||
colorizeTypes: colorizeDataTypes,
|
||||
typeKind,
|
||||
isNull: value === null,
|
||||
isNull: isNullCell,
|
||||
isDraft: item.isDraft && value === null,
|
||||
isEditing: isEditingThisCell,
|
||||
isControl: shouldRenderBooleanCheckbox,
|
||||
@@ -580,8 +583,8 @@ export function drawCanvasDataGrid(options: DrawCanvasDataGridOptions) {
|
||||
const cellTextColor = textRole === "muted" ? theme.mutedForeground : textRole === "type" ? dataGridTypeForeground(theme, typeKind) : theme.foreground;
|
||||
ctx.textAlign = isBooleanNullCell ? "center" : isRightAlign ? "right" : "left";
|
||||
ctx.fillStyle = cellTextColor;
|
||||
ctx.font = value === null ? italicFont : tabularFont;
|
||||
setCanvasNumericVariant(ctx, value === null ? "normal" : "tabular-nums");
|
||||
ctx.font = isNullCell ? italicFont : tabularFont;
|
||||
setCanvasNumericVariant(ctx, isNullCell ? "normal" : "tabular-nums");
|
||||
const reservedWidth = rightAlignedActionCell?.rowIndex === item.displayIndex && rightAlignedActionCell.visibleColIdx === visibleColIdx ? rightAlignedActionCell.reservedWidth : 0;
|
||||
const { textAnchorX, maxWidth: cellMaxWidth } = resolveCanvasCellTextLayout({ drawX, colWidth, dpr: scaleX, isRightAlign, reservedWidth });
|
||||
if (shouldRenderBooleanCheckbox) {
|
||||
|
||||
@@ -145,10 +145,18 @@ function mongoDocumentGridEscapedString(value: unknown): string | undefined {
|
||||
|
||||
/** Returns the text presented in a collection-grid editor, when customized. */
|
||||
export function mongoDocumentGridEditorText(value: unknown): string | undefined {
|
||||
if (value === MONGO_DOCUMENT_GRID_NULL) return "NULL";
|
||||
// An existing BSON null is represented as NULL in the grid, but editing it
|
||||
// starts with an empty input. The private marker must never be user-facing.
|
||||
if (value === MONGO_DOCUMENT_GRID_NULL) return "";
|
||||
return mongoDocumentGridEscapedString(value);
|
||||
}
|
||||
|
||||
/** Returns the text used when copying a collection-grid cell. */
|
||||
export function mongoDocumentGridClipboardText(value: unknown): string | undefined {
|
||||
if (value === MONGO_DOCUMENT_GRID_NULL) return "NULL";
|
||||
return mongoDocumentGridEditorText(value);
|
||||
}
|
||||
|
||||
/** Returns the custom display text required by collection-grid BSON values. */
|
||||
export function mongoDocumentGridDisplayText(value: unknown): string | undefined {
|
||||
if (value === MONGO_DOCUMENT_GRID_NULL) return "NULL";
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
MONGO_DOCUMENT_GRID_NULL,
|
||||
mongoDocumentDisplayValue,
|
||||
mongoDocumentGridDisplayText,
|
||||
mongoDocumentGridClipboardText,
|
||||
mongoDocumentGridEditorText,
|
||||
mongoDocumentGridExternalValue,
|
||||
mongoDocumentGridInputValue,
|
||||
@@ -403,6 +404,13 @@ test("escapes Mongo collection-grid values reserved for BSON null state", () =>
|
||||
});
|
||||
});
|
||||
|
||||
test("keeps BSON null empty in editors while preserving a copy marker", () => {
|
||||
assert.equal(mongoDocumentGridEditorText(MONGO_DOCUMENT_GRID_NULL), "");
|
||||
assert.equal(mongoDocumentGridClipboardText(MONGO_DOCUMENT_GRID_NULL), "NULL");
|
||||
assert.equal(mongoDocumentGridEditorText("NULL"), undefined);
|
||||
assert.equal(mongoDocumentGridClipboardText("NULL"), undefined);
|
||||
});
|
||||
|
||||
test("restores internal Mongo collection-grid values for external output", () => {
|
||||
assert.equal(mongoDocumentGridExternalValue(MONGO_DOCUMENT_GRID_NULL), null);
|
||||
assert.equal(mongoDocumentGridExternalValue("NULL"), "NULL");
|
||||
|
||||
Reference in New Issue
Block a user