Skip to content

Commit 87e2537

Browse files
committed
It now renders reasonably well
1 parent 5e550a9 commit 87e2537

1 file changed

Lines changed: 117 additions & 3 deletions

File tree

apps/webapp/app/components/code/TSQLResultsTable.tsx

Lines changed: 117 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ import { QueueName } from "../runs/v3/QueueName";
3333

3434
const MAX_STRING_DISPLAY_LENGTH = 64;
3535
const ROW_HEIGHT = 33; // Estimated row height in pixels
36-
const DEFAULT_COLUMN_SIZE = 150; // Default column width
36+
37+
// Column width calculation constants
38+
const MIN_COLUMN_WIDTH = 60;
39+
const MAX_COLUMN_WIDTH = 400;
40+
const CHAR_WIDTH_PX = 7.5; // Approximate width of a monospace character at text-xs (12px)
41+
const CELL_PADDING_PX = 40; // px-2 (8px) on each side + buffer for copy button
42+
const SAMPLE_SIZE = 100; // Number of rows to sample for width calculation
3743

3844
// Type for row data
3945
type RowData = Record<string, unknown>;
@@ -44,6 +50,113 @@ interface ColumnMeta {
4450
alignment: "left" | "right";
4551
}
4652

53+
/**
54+
* Get the approximate display length (in characters) of a value based on its type and formatting
55+
*/
56+
function getDisplayLength(value: unknown, column: OutputColumnMetadata): number {
57+
if (value === null) return 4; // "NULL"
58+
if (value === undefined) return 9; // "UNDEFINED"
59+
60+
// Handle custom render types - estimate their rendered width
61+
if (column.customRenderType) {
62+
switch (column.customRenderType) {
63+
case "runId":
64+
// Run IDs are typically like "run_abc123xyz"
65+
return typeof value === "string" ? Math.min(value.length, MAX_STRING_DISPLAY_LENGTH) : 15;
66+
case "runStatus":
67+
// Status badges have icon + text, approximate width
68+
return 12;
69+
case "duration":
70+
if (typeof value === "number") {
71+
// Format and measure: "1h 23m 45s" style
72+
const formatted = formatDurationMilliseconds(value, { style: "short" });
73+
return formatted.length;
74+
}
75+
return 10;
76+
case "durationSeconds":
77+
if (typeof value === "number") {
78+
const formatted = formatDurationMilliseconds(value * 1000, { style: "short" });
79+
return formatted.length;
80+
}
81+
return 10;
82+
case "cost":
83+
case "costInDollars":
84+
// Currency format: "$1,234.56"
85+
if (typeof value === "number") {
86+
const amount = column.customRenderType === "cost" ? value / 100 : value;
87+
return formatCurrencyAccurate(amount).length;
88+
}
89+
return 12;
90+
case "machine":
91+
// Machine preset names like "small-1x"
92+
return typeof value === "string" ? value.length : 10;
93+
case "environmentType":
94+
// Environment labels: "PRODUCTION", "STAGING", etc.
95+
return 12;
96+
case "project":
97+
case "environment":
98+
return typeof value === "string" ? Math.min(value.length, 20) : 12;
99+
case "queue":
100+
return typeof value === "string" ? Math.min(value.length, 25) : 15;
101+
}
102+
}
103+
104+
// Handle by ClickHouse type
105+
if (isDateTimeType(column.type)) {
106+
// DateTime format: "Jan 15, 2026, 12:34:56 PM"
107+
return 24;
108+
}
109+
110+
if (column.type === "JSON" || column.type.startsWith("Array")) {
111+
if (typeof value === "object") {
112+
const jsonStr = JSON.stringify(value);
113+
return Math.min(jsonStr.length, MAX_STRING_DISPLAY_LENGTH);
114+
}
115+
}
116+
117+
if (isBooleanType(column.type)) {
118+
return 5; // "true" or "false"
119+
}
120+
121+
if (isNumericType(column.type)) {
122+
if (typeof value === "number") {
123+
return formatNumber(value).length;
124+
}
125+
}
126+
127+
// Default: string length capped at max display length
128+
const strValue = String(value);
129+
return Math.min(strValue.length, MAX_STRING_DISPLAY_LENGTH);
130+
}
131+
132+
/**
133+
* Calculate the optimal width for a column based on its content
134+
*/
135+
function calculateColumnWidth(
136+
columnName: string,
137+
rows: RowData[],
138+
column: OutputColumnMetadata
139+
): number {
140+
// Start with header length
141+
let maxLength = columnName.length;
142+
143+
// Sample rows to find max content length
144+
const sampleRows = rows.slice(0, SAMPLE_SIZE);
145+
for (const row of sampleRows) {
146+
const value = row[columnName];
147+
const displayLength = getDisplayLength(value, column);
148+
if (displayLength > maxLength) {
149+
maxLength = displayLength;
150+
}
151+
}
152+
153+
// Calculate pixel width: characters * char width + padding
154+
const calculatedWidth = Math.ceil(maxLength * CHAR_WIDTH_PX + CELL_PADDING_PX);
155+
156+
// Apply min/max bounds
157+
return Math.min(MAX_COLUMN_WIDTH, Math.max(MIN_COLUMN_WIDTH, calculatedWidth));
158+
}
159+
47160
/**
48161
* Truncate a string for display, adding ellipsis if it exceeds max length
49162
*/
@@ -526,6 +639,7 @@ export const TSQLResultsTable = memo(function TSQLResultsTable({
526639
const tableContainerRef = useRef<HTMLDivElement>(null);
527640

528641
// Create TanStack Table column definitions from OutputColumnMetadata
642+
// Calculate column widths based on content
529643
const columnDefs = useMemo<ColumnDef<RowData, unknown>[]>(
530644
() =>
531645
columns.map((col) => ({
@@ -543,9 +657,9 @@ export const TSQLResultsTable = memo(function TSQLResultsTable({
543657
outputColumn: col,
544658
alignment: isRightAlignedColumn(col) ? "right" : "left",
545659
} as ColumnMeta,
546-
size: DEFAULT_COLUMN_SIZE,
660+
size: calculateColumnWidth(col.name, rows, col),
547661
})),
548-
[columns, prettyFormatting]
662+
[columns, rows, prettyFormatting]
549663
);
550664

551665
// Initialize TanStack Table

0 commit comments

Comments
 (0)