mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-29 00:09:52 +00:00
99 lines
3.1 KiB
TypeScript
99 lines
3.1 KiB
TypeScript
export type CellDetailTab = "details" | "valueEditor";
|
|
export type ValueEditorAction = "formatJson" | "setNull" | "restoreOriginal";
|
|
|
|
export const CELL_DETAIL_JSON_FORMAT_MAX_LENGTH = 50_000;
|
|
|
|
export interface CellDetailPresentationOptions {
|
|
isEditable: boolean;
|
|
}
|
|
|
|
export interface LinkedCellDetailOptions {
|
|
isOpen: boolean;
|
|
isEditing: boolean;
|
|
selectedCell: { rowIndex: number; visibleColIndex: number } | null;
|
|
actualColumnIndex: (visibleColIndex: number) => number;
|
|
}
|
|
|
|
export interface CellDetailTarget {
|
|
rowIndex: number;
|
|
col: number;
|
|
}
|
|
|
|
export function defaultCellDetailTab(): CellDetailTab {
|
|
return "details";
|
|
}
|
|
|
|
export function visibleCellDetailTabs(options: CellDetailPresentationOptions): CellDetailTab[] {
|
|
const tabs: CellDetailTab[] = ["details"];
|
|
if (options.isEditable) {
|
|
tabs.push("valueEditor");
|
|
}
|
|
return tabs;
|
|
}
|
|
|
|
export function cellDetailEditorText(value: unknown, _columnType?: string): string {
|
|
if (value === null) return "";
|
|
return cellDetailRawEditorText(value);
|
|
}
|
|
|
|
function cellDetailRawEditorText(value: unknown): string {
|
|
if (typeof value === "object") return JSON.stringify(value);
|
|
return String(value);
|
|
}
|
|
|
|
export function valueEditorActions(options: { canSetNull: boolean; canFormatJson?: boolean }): ValueEditorAction[] {
|
|
const actions: ValueEditorAction[] = [];
|
|
if (options.canFormatJson) actions.push("formatJson");
|
|
if (options.canSetNull) actions.push("setNull");
|
|
actions.push("restoreOriginal");
|
|
return actions;
|
|
}
|
|
|
|
export function linkedCellDetailTarget(options: LinkedCellDetailOptions): CellDetailTarget | null {
|
|
if (!options.isOpen || options.isEditing || !options.selectedCell) return null;
|
|
return {
|
|
rowIndex: options.selectedCell.rowIndex,
|
|
col: options.actualColumnIndex(options.selectedCell.visibleColIndex),
|
|
};
|
|
}
|
|
|
|
export function isJsonColumnType(columnType: string | undefined): boolean {
|
|
const base = (columnType ?? "")
|
|
.trim()
|
|
.toLowerCase()
|
|
.split(/[(:\s]/)[0];
|
|
return base === "json" || base === "jsonb";
|
|
}
|
|
|
|
export function isGeometryColumnType(columnType: string | undefined): boolean {
|
|
const base = (columnType ?? "")
|
|
.trim()
|
|
.toLowerCase()
|
|
.split(/[(:\s]/)[0];
|
|
return base === "geometry" || base === "geography";
|
|
}
|
|
|
|
export function canFormatCellDetailJson(value: unknown, columnType?: string): boolean {
|
|
if (value === null || value === undefined) return false;
|
|
const text = cellDetailRawEditorText(value);
|
|
if (text.length > CELL_DETAIL_JSON_FORMAT_MAX_LENGTH) return false;
|
|
if (isJsonColumnType(columnType)) return !!formatJsonText(text);
|
|
return typeof value === "string" && looksLikeJsonContainer(text) && !!formatJsonText(text);
|
|
}
|
|
|
|
export function formatJsonText(text: string): string | undefined {
|
|
const trimmed = text.trim();
|
|
if (!trimmed) return undefined;
|
|
if (trimmed.length > CELL_DETAIL_JSON_FORMAT_MAX_LENGTH) return undefined;
|
|
try {
|
|
return JSON.stringify(JSON.parse(trimmed), null, 2);
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
function looksLikeJsonContainer(text: string): boolean {
|
|
const trimmed = text.trim();
|
|
return trimmed.startsWith("{") || trimmed.startsWith("[");
|
|
}
|