feat(grid): preview text stored in MySQL BLOB cells
This commit is contained in:
8 files changed
+217
-15
No files matched your search
@@ -141,6 +141,8 @@ import {
|
||||
canDownloadBinaryCellValue,
|
||||
downloadBinaryCellPayload,
|
||||
formatBinaryCellByteSize,
|
||||
binaryCellUtf8Text,
|
||||
isBlobCellColumnType,
|
||||
isBinaryCellColumnType,
|
||||
openBinaryCellFile,
|
||||
parseBinaryCellBytes,
|
||||
@@ -4120,8 +4122,8 @@ function cellEditContentNeedsExpandedEditor(options: { displayText: string; edit
|
||||
function cellEditorTextForValue(value: CellValue | undefined, columnIndex: number): string {
|
||||
return dataGridCellEditorText({
|
||||
value: value ?? null,
|
||||
databaseType: props.databaseType,
|
||||
columnInfo: tableColumnForGridColumn(columnIndex),
|
||||
databaseType: resolvedDatabaseType.value,
|
||||
columnInfo: tableColumnForGridColumn(columnIndex) ?? resultColumnInfoForGridColumn(columnIndex),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4185,7 +4187,7 @@ function isIoTDBTimestampColumn(columnIndex: number): boolean {
|
||||
}
|
||||
|
||||
function inlineCellEditorText(value: CellValue, columnIndex: number): string {
|
||||
const columnInfo = tableColumnForGridColumn(columnIndex);
|
||||
const columnInfo = tableColumnForGridColumn(columnIndex) ?? resultColumnInfoForGridColumn(columnIndex);
|
||||
const columnType = props.result.column_types?.[columnIndex] ?? columnInfo?.data_type;
|
||||
return (
|
||||
formatIoTDBTimestampEditorValue(value, resolvedDatabaseType.value, columnType, resolvedConnectionConfig.value?.url_params) ??
|
||||
@@ -6054,8 +6056,8 @@ watch(activeCellDetail, (detail) => {
|
||||
}
|
||||
const value = dataGridCellEditorText({
|
||||
value: detail.value,
|
||||
databaseType: props.databaseType,
|
||||
columnInfo: tableColumnForGridColumn(detail.colIndex),
|
||||
databaseType: resolvedDatabaseType.value,
|
||||
columnInfo: tableColumnForGridColumn(detail.colIndex) ?? resultColumnInfoForGridColumn(detail.colIndex),
|
||||
});
|
||||
detailEditValue.value = value;
|
||||
detailEditOriginalValue.value = value;
|
||||
@@ -9100,7 +9102,7 @@ async function copyDetailValue() {
|
||||
if (!initialDetail || !(await hydrateLargeValueCell(initialDetail.rowId, initialDetail.colIndex))) return;
|
||||
const detail = activeCellDetail.value;
|
||||
if (!detail) return;
|
||||
const text = detail.value === null ? "" : displayCellValue(detail.value);
|
||||
const text = detail.value === null ? "" : isBlobCellColumnType(detail.type) ? (binaryCellUtf8Text(detail.value, detail.type, resolvedDatabaseType.value) ?? displayCellValue(detail.value)) : displayCellValue(detail.value);
|
||||
copyText(text);
|
||||
}
|
||||
|
||||
@@ -13356,6 +13358,7 @@ function currentGridContextMenuItems(): ContextMenuItem[] {
|
||||
:import-binary-value="importDetailBinaryValue"
|
||||
:open-image-preview="openImagePreview"
|
||||
:can-copy-sql-condition="canCopyPreparedDetailSqlCondition"
|
||||
:database-type="resolvedDatabaseType"
|
||||
@start-edit="startDetailEdit"
|
||||
@compact-json="compactDetailJson"
|
||||
@toggle-formatted="toggleCellDetailJsonFormatted"
|
||||
@@ -13545,6 +13548,7 @@ function currentGridContextMenuItems(): ContextMenuItem[] {
|
||||
:download-binary-value="downloadDetailBinaryValue"
|
||||
:can-import-binary-value="canImportDetailBinaryValue"
|
||||
:import-binary-value="importDetailBinaryValue"
|
||||
:database-type="resolvedDatabaseType"
|
||||
@edit="openDialogCellInSidePanel"
|
||||
/>
|
||||
|
||||
|
||||
@@ -9,10 +9,11 @@ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover
|
||||
import { useCellDetailEditor, type UseCellDetailEditorReturn } from "@/composables/useCellDetailEditor";
|
||||
import { useTheme } from "@/composables/useTheme";
|
||||
import { useSettingsStore } from "@/stores/settingsStore";
|
||||
import { BINARY_CELL_DOWNLOAD_MODES, type BinaryCellDownloadMode } from "@/lib/dataGrid/binaryCellDownload";
|
||||
import { BINARY_CELL_DOWNLOAD_MODES, binaryCellUtf8Text, isBlobCellColumnType, type BinaryCellDownloadMode } from "@/lib/dataGrid/binaryCellDownload";
|
||||
import { isGeometryColumnType } from "@/lib/dataGrid/cellDetailPresentation";
|
||||
import { isHexGeometry, renderWktOnCanvas } from "@/lib/dataGrid/geometryPreview";
|
||||
import type { DataGridCellDetail } from "@/lib/dataGrid/dataGridDetail";
|
||||
import type { DatabaseType } from "@/types/database";
|
||||
|
||||
const { t } = useI18n();
|
||||
const settingsStore = useSettingsStore();
|
||||
@@ -27,6 +28,8 @@ const props = defineProps<{
|
||||
downloadBinaryValue: (detail: DataGridCellDetail | null, mode: BinaryCellDownloadMode) => void | Promise<void>;
|
||||
canImportBinaryValue: (detail: DataGridCellDetail | null) => boolean;
|
||||
importBinaryValue: (detail: DataGridCellDetail | null) => void | Promise<void>;
|
||||
/** BLOB 文本预览与编辑写回一致,仅在 MySQL 连接开启。 */
|
||||
databaseType?: DatabaseType;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -41,6 +44,8 @@ let jsonPreviewEditor: UseCellDetailEditorReturn | null = null;
|
||||
|
||||
const jsonFormatted = computed(() => settingsStore.editorSettings.cellDetailJsonFormatted);
|
||||
const jsonView = computed(() => jsonFormatted.value && !!props.detail?.formattedJson);
|
||||
const binaryTextPreview = computed(() => (props.detail && isBlobCellColumnType(props.detail.type) ? binaryCellUtf8Text(props.detail.value, props.detail.type, props.databaseType) : null));
|
||||
const presentedValuePreview = computed(() => (binaryTextPreview.value === null ? props.detail?.rawValuePreview : props.detail?.displayValuePreview) ?? "");
|
||||
|
||||
function toggleJsonFormatted() {
|
||||
settingsStore.updateEditorSettings({ cellDetailJsonFormatted: !jsonFormatted.value });
|
||||
@@ -53,7 +58,7 @@ function copyCurrentValue() {
|
||||
props.copyText(detail.formattedJson);
|
||||
return;
|
||||
}
|
||||
props.copyText(detail.value === null ? "" : detail.rawValue);
|
||||
props.copyText(detail.value === null ? "" : (binaryTextPreview.value ?? detail.rawValue));
|
||||
}
|
||||
|
||||
function copyColumnName() {
|
||||
@@ -186,7 +191,7 @@ watch(
|
||||
<img :src="detail.imagePreviewUrl" :alt="detail.column" loading="lazy" decoding="async" referrerpolicy="no-referrer" class="max-h-72 w-full object-contain" />
|
||||
</a>
|
||||
<div v-if="jsonView && detail.formattedJson" ref="jsonPreviewContainer" data-cell-detail-editor-root class="h-[44vh] min-h-60 overflow-hidden rounded border bg-muted/20 p-3" />
|
||||
<pre v-else class="dbx-data-grid-value-font max-h-[44vh] overflow-auto rounded border bg-muted/20 p-3 text-xs whitespace-pre-wrap break-words" :class="{ 'italic text-muted-foreground': detail.value === null }">{{ detail.rawValuePreview }}</pre>
|
||||
<pre v-else class="dbx-data-grid-value-font max-h-[44vh] overflow-auto rounded border bg-muted/20 p-3 text-xs whitespace-pre-wrap break-words" :class="{ 'italic text-muted-foreground': detail.value === null }">{{ presentedValuePreview }}</pre>
|
||||
<div v-if="detail.isValuePreviewTruncated && !jsonView" class="text-[11px] text-muted-foreground">
|
||||
{{ t("grid.largeValuePreviewHint", { count: detail.rawValuePreview.length }) }}
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { toRef } from "vue";
|
||||
import { computed, toRef } from "vue";
|
||||
import { Code2, Copy, Download, Eye, FileUp, Pencil, X } from "@lucide/vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -8,11 +8,12 @@ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover
|
||||
import { TabsContent } from "@/components/ui/tabs";
|
||||
import TemporalCellEditor from "@/components/grid/TemporalCellEditor.vue";
|
||||
import { useDataGridCellDetail } from "@/composables/useDataGridCellDetail";
|
||||
import { BINARY_CELL_DOWNLOAD_MODES, type BinaryCellDownloadMode } from "@/lib/dataGrid/binaryCellDownload";
|
||||
import { BINARY_CELL_DOWNLOAD_MODES, binaryCellUtf8Text, isBlobCellColumnType, type BinaryCellDownloadMode } from "@/lib/dataGrid/binaryCellDownload";
|
||||
import { isGeometryColumnType } from "@/lib/dataGrid/cellDetailPresentation";
|
||||
import { isHexGeometry } from "@/lib/dataGrid/geometryPreview";
|
||||
import type { DataGridCellDetail } from "@/lib/dataGrid/dataGridDetail";
|
||||
import type { TemporalCellEditorConfig } from "@/lib/dataGrid/dataGridTemporalEditor";
|
||||
import type { DatabaseType } from "@/types/database";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -34,6 +35,8 @@ const props = defineProps<{
|
||||
importBinaryValue: (detail: DataGridCellDetail | null) => void | Promise<void>;
|
||||
openImagePreview: (src: string, title: string) => void;
|
||||
canCopySqlCondition: () => boolean;
|
||||
/** BLOB 文本预览与编辑写回一致,仅在 MySQL 连接开启。 */
|
||||
databaseType?: DatabaseType;
|
||||
}>();
|
||||
|
||||
const detailEditValue = defineModel<string>("value", { default: "" });
|
||||
@@ -55,6 +58,9 @@ void geometryCanvas;
|
||||
void detailsEditorContainer;
|
||||
void sideJsonPreviewContainer;
|
||||
|
||||
const binaryTextPreview = computed(() => (isBlobCellColumnType(props.detail.type) ? binaryCellUtf8Text(props.detail.value, props.detail.type, props.databaseType) : null));
|
||||
const presentedValuePreview = computed(() => (binaryTextPreview.value === null ? props.detail.rawValuePreview : props.detail.displayValuePreview));
|
||||
|
||||
function startJsonEditFromBlankArea(event: MouseEvent) {
|
||||
const target = event.target as { closest?: (selector: string) => Element | null } | null;
|
||||
const line = target?.closest?.(".cm-line");
|
||||
@@ -181,7 +187,7 @@ defineExpose({ openSearch });
|
||||
class="dbx-data-grid-value-font overflow-auto rounded border bg-muted/20 p-2 text-xs whitespace-pre-wrap break-words cursor-pointer hover:border-primary/50"
|
||||
:class="[{ 'cursor-text': detail.isEditable }, panelIsBottom && detail.imagePreviewUrl ? 'min-h-24 max-h-32 shrink-0' : '', valueFillsHeight && !detail.imagePreviewUrl ? 'min-h-0 flex-1' : '']"
|
||||
@dblclick="emit('startEdit')"
|
||||
>{{ detail.rawValuePreview }}</pre
|
||||
>{{ presentedValuePreview }}</pre
|
||||
>
|
||||
<div v-if="detail.isValuePreviewTruncated && !sideJsonView" class="text-[11px] text-muted-foreground">{{ t("grid.largeValuePreviewHint", { count: detail.rawValuePreview.length }) }}</div>
|
||||
</div>
|
||||
|
||||
@@ -1184,6 +1184,105 @@ describe("DataGridTextFilterWorkbench", () => {
|
||||
});
|
||||
|
||||
describe("cell detail surfaces", () => {
|
||||
it("presents printable LONG BLOB bytes as text and copies the presented value", async () => {
|
||||
const copyText = vi.fn();
|
||||
const blobDetail = detail({
|
||||
type: "LONGBLOB",
|
||||
value: "0x2332303035383035",
|
||||
rawValue: "0x2332303035383035",
|
||||
rawValuePreview: "0x2332303035383035",
|
||||
displayValue: "#2005805",
|
||||
displayValuePreview: "#2005",
|
||||
formattedJson: "",
|
||||
});
|
||||
const dialog = mountComponent(DataGridCellDetailDialog, {
|
||||
open: true,
|
||||
detail: blobDetail,
|
||||
typeColorClass: () => "",
|
||||
openImagePreview: vi.fn(),
|
||||
copyText,
|
||||
canDownloadBinaryValue: () => true,
|
||||
downloadBinaryValue: vi.fn(),
|
||||
canImportBinaryValue: () => false,
|
||||
importBinaryValue: vi.fn(),
|
||||
databaseType: "mysql",
|
||||
});
|
||||
const panel = mountComponent(DataGridCellDetailPanel, {
|
||||
detail: blobDetail,
|
||||
panelIsBottom: true,
|
||||
metadataCollapsed: false,
|
||||
valueFillsHeight: false,
|
||||
editing: false,
|
||||
sideJsonView: false,
|
||||
showCompactJson: false,
|
||||
canCompactJson: false,
|
||||
typeColorClass: () => "",
|
||||
canDownloadBinaryValue: () => true,
|
||||
downloadBinaryValue: vi.fn(),
|
||||
canImportBinaryValue: () => false,
|
||||
importBinaryValue: vi.fn(),
|
||||
openImagePreview: vi.fn(),
|
||||
canCopySqlCondition: () => true,
|
||||
databaseType: "mysql",
|
||||
});
|
||||
|
||||
expect(hostText(dialog.root)).toContain("#2005");
|
||||
expect(hostText(dialog.root)).not.toContain("#2005805");
|
||||
expect(hostText(panel.root)).toContain("#2005");
|
||||
expect(hostText(panel.root)).not.toContain("#2005805");
|
||||
dispatch(
|
||||
findOne(dialog.root, (node) => node.props.title === "grid.copyValue"),
|
||||
"click",
|
||||
);
|
||||
expect(copyText).toHaveBeenCalledWith("#2005805");
|
||||
});
|
||||
|
||||
it("keeps non-MySQL BLOB detail previews in hex", () => {
|
||||
const panel = mountComponent(DataGridCellDetailPanel, {
|
||||
detail: detail({ type: "BLOB", value: "0x2332303035383035", rawValue: "0x2332303035383035", rawValuePreview: "0x2332303035383035", displayValue: "BLOB [8 bytes]", displayValuePreview: "BLOB [8 bytes]", formattedJson: "" }),
|
||||
panelIsBottom: true,
|
||||
metadataCollapsed: false,
|
||||
valueFillsHeight: false,
|
||||
editing: false,
|
||||
sideJsonView: false,
|
||||
showCompactJson: false,
|
||||
canCompactJson: false,
|
||||
typeColorClass: () => "",
|
||||
canDownloadBinaryValue: () => true,
|
||||
downloadBinaryValue: vi.fn(),
|
||||
canImportBinaryValue: () => false,
|
||||
importBinaryValue: vi.fn(),
|
||||
openImagePreview: vi.fn(),
|
||||
canCopySqlCondition: () => true,
|
||||
databaseType: "sqlite",
|
||||
});
|
||||
|
||||
expect(hostText(panel.root)).toContain("0x2332303035383035");
|
||||
});
|
||||
|
||||
it("keeps non-text LONG BLOB values in hex", () => {
|
||||
const panel = mountComponent(DataGridCellDetailPanel, {
|
||||
detail: detail({ type: "LONGBLOB", value: "0x89504e470d0a1a0a", rawValue: "0x89504e470d0a1a0a", rawValuePreview: "0x89504e470d0a1a0a", formattedJson: "" }),
|
||||
panelIsBottom: true,
|
||||
metadataCollapsed: false,
|
||||
valueFillsHeight: false,
|
||||
editing: false,
|
||||
sideJsonView: false,
|
||||
showCompactJson: false,
|
||||
canCompactJson: false,
|
||||
typeColorClass: () => "",
|
||||
canDownloadBinaryValue: () => true,
|
||||
downloadBinaryValue: vi.fn(),
|
||||
canImportBinaryValue: () => false,
|
||||
importBinaryValue: vi.fn(),
|
||||
openImagePreview: vi.fn(),
|
||||
canCopySqlCondition: () => true,
|
||||
databaseType: "mysql",
|
||||
});
|
||||
|
||||
expect(hostText(panel.root)).toContain("0x89504e470d0a1a0a");
|
||||
});
|
||||
|
||||
it("copies the presented value, emits edit, closes, and replaces the JSON result", async () => {
|
||||
const copyText = vi.fn();
|
||||
const edit = vi.fn();
|
||||
|
||||
@@ -114,6 +114,7 @@ const HEX_ESCAPE_RE = /^(?:\\x[0-9a-fA-F]{2}|\s)+$/;
|
||||
const BINARY_TYPE_RE = /^(?:blob|tinyblob|mediumblob|longblob|bytea|bytes|binary|varbinary|image|raw|long\s+raw)(?:\b|\()/i;
|
||||
const FIXED_BINARY_TYPE_RE = /^binary(?:\b|\()/i;
|
||||
const BINARY_STRING_TYPE_RE = /^(?:binary|varbinary)(?:\b|\()/i;
|
||||
const BLOB_TYPE_RE = /^(?:blob|tinyblob|mediumblob|longblob)(?:\b|\()/i;
|
||||
const MYSQL_FILE_IMPORT_TYPE_RE = /^(?:blob|tinyblob|mediumblob|longblob|binary|varbinary)(?:\b|\()/i;
|
||||
|
||||
function copyBytesForBlob(bytes: Uint8Array): Uint8Array<ArrayBuffer> {
|
||||
@@ -182,6 +183,11 @@ export function isBinaryCellColumnType(columnType?: string): boolean {
|
||||
return !!type && BINARY_TYPE_RE.test(type);
|
||||
}
|
||||
|
||||
export function isBlobCellColumnType(columnType?: string): boolean {
|
||||
const type = (columnType ?? "").trim();
|
||||
return !!type && BLOB_TYPE_RE.test(type);
|
||||
}
|
||||
|
||||
export function canImportBinaryCellFile(databaseType?: DatabaseType, columnType?: string): boolean {
|
||||
const type = (columnType ?? "").trim();
|
||||
if (databaseType === "postgres") return /^bytea(?:\b|\()/i.test(type);
|
||||
@@ -200,14 +206,34 @@ export function binaryCellDisplayText(value: unknown, columnType?: string, origi
|
||||
}
|
||||
const bytes = parseBinaryCellBytes(value, columnType, databaseType);
|
||||
if (!bytes || !isBinaryCellColumnType(columnType)) return null;
|
||||
if (BINARY_STRING_TYPE_RE.test((columnType ?? "").trim())) {
|
||||
const previewBytes = FIXED_BINARY_TYPE_RE.test((columnType ?? "").trim()) ? trimTrailingNullBytes(bytes) : bytes;
|
||||
const text = printableUtf8Text(previewBytes);
|
||||
if (isBinaryCellTextPreviewColumn(columnType, databaseType)) {
|
||||
const text = binaryCellUtf8TextBytes(bytes, columnType);
|
||||
if (text !== null) return text;
|
||||
}
|
||||
return `${binaryCellDisplayLabel(columnType)} [${formatBinaryCellByteSize(bytes.length)}]`;
|
||||
}
|
||||
|
||||
export function binaryCellUtf8Text(value: unknown, columnType?: string, databaseType?: DatabaseType): string | null {
|
||||
if (!isBinaryCellColumnType(columnType) || !isBinaryCellTextPreviewColumn(columnType, databaseType)) return null;
|
||||
const bytes = parseBinaryCellBytes(value, columnType, databaseType);
|
||||
if (!bytes) return null;
|
||||
return binaryCellUtf8TextBytes(bytes, columnType);
|
||||
}
|
||||
|
||||
// MySQL BLOB 的文本预览必须与编辑写回路径(coerceMysqlBlobTextValue,仅 mysql)走同一闸门:
|
||||
// SQLite/DuckDB/H2/Firebird 等库同样暴露 `blob` 列,若一律按文本预览会出现
|
||||
// “显示是文本、编辑器却是十六进制”的不一致,故 blob 文本预览仅在 mysql 连接开启。
|
||||
// binary/varbinary 的文本预览早于该特性存在(如 TDengine BINARY 文本),保持全库通用。
|
||||
function isBinaryCellTextPreviewColumn(columnType: string | undefined, databaseType: DatabaseType | undefined): boolean {
|
||||
if (BINARY_STRING_TYPE_RE.test((columnType ?? "").trim())) return true;
|
||||
return isBlobCellColumnType(columnType) && databaseType === "mysql";
|
||||
}
|
||||
|
||||
function binaryCellUtf8TextBytes(bytes: Uint8Array, columnType?: string): string | null {
|
||||
const previewBytes = FIXED_BINARY_TYPE_RE.test((columnType ?? "").trim()) ? trimTrailingNullBytes(bytes) : bytes;
|
||||
return printableUtf8Text(previewBytes);
|
||||
}
|
||||
|
||||
function trimTrailingNullBytes(bytes: Uint8Array): Uint8Array {
|
||||
let end = bytes.length;
|
||||
while (end > 0 && bytes[end - 1] === 0) end--;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { GridCellValue } from "@/lib/dataGrid/dataGridSql";
|
||||
import type { DatabaseType, ColumnInfo } from "@/types/database";
|
||||
import { binaryCellBytesToHexValue, binaryCellUtf8Text, isBlobCellColumnType } from "@/lib/dataGrid/binaryCellDownload";
|
||||
import { isNumericColumnType } from "@/lib/dataGrid/dataGridColumnType";
|
||||
import { isBooleanColumnType } from "@/lib/dataGrid/dataGridBooleanColumn";
|
||||
|
||||
@@ -17,6 +18,8 @@ export function coerceDataGridCellValue(options: CoerceDataGridCellValueOptions)
|
||||
const { value, oldValue } = options;
|
||||
if (value === "" && options.emptyStringAsNull) return null;
|
||||
if (value === "" && oldValue === null && !options.preserveEmptyString) return null;
|
||||
const blobValue = coerceMysqlBlobTextValue(options);
|
||||
if (blobValue !== undefined) return blobValue;
|
||||
const postgresArrayValue = coercePostgresArrayValue(options);
|
||||
if (postgresArrayValue !== undefined) return postgresArrayValue;
|
||||
// Excel-pasted values often carry thousands separators (10,000.00) that make
|
||||
@@ -68,12 +71,24 @@ function parseBooleanInput(value: string, allowNumericAliases: boolean): boolean
|
||||
export function dataGridCellEditorText(options: { value: GridCellValue | undefined; databaseType: DatabaseType | undefined; columnInfo: Pick<ColumnInfo, "data_type"> | undefined }): string {
|
||||
const value = options.value ?? null;
|
||||
if (value === null) return "";
|
||||
if (options.databaseType === "mysql" && isBlobCellColumnType(options.columnInfo?.data_type)) {
|
||||
const text = binaryCellUtf8Text(value, options.columnInfo?.data_type, options.databaseType);
|
||||
if (text !== null) return text;
|
||||
}
|
||||
if (Array.isArray(value) && options.databaseType === "postgres" && isPostgresArrayColumn(options.columnInfo, value)) {
|
||||
return formatPostgresArrayText(value);
|
||||
}
|
||||
return typeof value === "object" ? JSON.stringify(value) : String(value);
|
||||
}
|
||||
|
||||
function coerceMysqlBlobTextValue(options: CoerceDataGridCellValueOptions): GridCellValue | undefined {
|
||||
if (options.databaseType !== "mysql" || !isBlobCellColumnType(options.columnInfo?.data_type)) return undefined;
|
||||
const originalText = binaryCellUtf8Text(options.oldValue, options.columnInfo?.data_type, options.databaseType);
|
||||
if (originalText === null) return undefined;
|
||||
if (options.value === originalText) return options.oldValue;
|
||||
return binaryCellBytesToHexValue(new TextEncoder().encode(options.value));
|
||||
}
|
||||
|
||||
export function dataGridCellDisplayText(options: { value: GridCellValue; databaseType: DatabaseType | undefined; columnInfo: Pick<ColumnInfo, "data_type"> | undefined }): string | undefined {
|
||||
if (Array.isArray(options.value) && options.databaseType === "postgres" && isPostgresArrayColumn(options.columnInfo, options.value)) {
|
||||
return formatPostgresArrayText(options.value);
|
||||
|
||||
Reference in new issue
Block a user