diff --git a/src/components/gui/export/export-result-button.tsx b/src/components/gui/export/export-result-button.tsx index 6ab4388f..64a245f0 100644 --- a/src/components/gui/export/export-result-button.tsx +++ b/src/components/gui/export/export-result-button.tsx @@ -17,7 +17,15 @@ import OptimizeTableState, { } from "../table-optimized/optimize-table-state"; export type ExportTarget = "clipboard" | "file"; -export type ExportFormat = "csv" | "delimited" | "json" | "sql" | "xlsx"; +export type ExportFormat = + | "csv" + | "delimited" + | "json" + | "sql" + | "xlsx" + | "xml" + | "md" + | "tsv"; export type ExportSelection = | "complete" | "selected_row" @@ -319,6 +327,33 @@ export default function ExportResultButton({ Excel +
+ + +
+
+ + +
+
+ + +
diff --git a/src/lib/export-helper.ts b/src/lib/export-helper.ts index fbb1b0e5..8b11605f 100644 --- a/src/lib/export-helper.ts +++ b/src/lib/export-helper.ts @@ -187,6 +187,81 @@ export function exportDataAsDelimitedText( return content; } +export function exportRowsToXml( + headers: string[], + records: unknown[][], + exportTarget?: ExportTarget +): string { + const escapeXml = (unsafe: string) => + unsafe + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + + let xml = '\n\n'; + + for (const record of records) { + xml += " \n"; + for (let i = 0; i < headers.length; i++) { + const header = escapeXml(headers[i]); + const value = record[i] != null ? escapeXml(String(record[i])) : ""; + xml += ` <${header}>${value}\n`; + } + xml += " \n"; + } + + xml += ""; + + if (exportTarget === "clipboard") { + copyToClipboard(xml); + return ""; + } + + return xml; +} + +export function exportToMarkdown( + headers: string[], + records: unknown[][], + exportTarget?: ExportTarget +) { + let result = `| ${headers.join(" | ")} | \n`; + result += `| ${headers.map(() => "---").join(" | ")} | \n`; + + for (const record of records) { + const row = record.map((value) => `| ${value} `).join(" ") + " |"; + result += row + "\n"; + } + + if (exportTarget === "clipboard") { + copyToClipboard(result); + return ""; + } + + return result; +} + +export function exportToTSV( + headers: string[], + records: unknown[][], + exportTarget?: ExportTarget +) { + let result = `${headers.join("\t")}\n`; + for (const record of records) { + const row = record.join("\t"); + result += row + "\n"; + } + + if (exportTarget === "clipboard") { + copyToClipboard(result); + return ""; + } + + return result; +} + export function getFormatHandlers( data: OptimizeTableState, exportTarget: ExportTarget, @@ -278,6 +353,9 @@ export function getFormatHandlers( exportTarget, exportOptions?.nullValue || "NULL" ), + xml: () => exportRowsToXml(headers, records, exportTarget), + md: () => exportToMarkdown(headers, records, exportTarget), + tsv: () => exportToTSV(headers, records, exportTarget), }; } @@ -310,6 +388,14 @@ export async function exportTableData( exportTarget: ExportTarget, options?: ExportOptions ): Promise { + console.log( + "Exporting", + schemaName, + tableName, + format, + exportTarget, + options + ); const result = await databaseDriver.query( `SELECT * FROM ${databaseDriver.escapeId(schemaName)}.${databaseDriver.escapeId(tableName)}` ); @@ -338,6 +424,9 @@ export async function exportTableData( options?.encloser || '"', exportTarget ), + xml: () => exportRowsToXml(headers, records, exportTarget), + md: () => exportToMarkdown(headers, records, exportTarget), + tsv: () => exportToTSV(headers, records, exportTarget), }; const handler = formatHandlers[format];