download-table.ts1.6 KBView on GitHub
/**
 * "Download as Excel" / "Download as CSV" for a table document.
 *
 * The server side is the whole implementation — `documents.exportTable` runs exceljs and
 * returns `{ filename, mimeType, base64 }`. All that is left on the client is turning that
 * into a Blob and clicking a link, which is here rather than inline in two callers (the
 * table toolbar and the `FileEditor` dropdown) so the two cannot produce differently-named
 * files.
 */

import { base64ToUint8Array } from '@/modules/documents/yjs';

export type TableExportFormat = 'xlsx' | 'csv';

export interface TableExportResult {
  filename: string;
  mimeType: string;
  base64: string;
}

/** The client surface `saveTableExport` needs — narrowed so callers can pass a test double. */
export interface TableExportClient {
  documents: {
    exportTable: {
      query: (input: {
        documentId: string;
        format: TableExportFormat;
      }) => Promise<TableExportResult>;
    };
  };
}

export async function downloadTableExport(
  client: TableExportClient,
  documentId: string,
  format: TableExportFormat,
): Promise<void> {
  const result = await client.documents.exportTable.query({ documentId, format });
  const blob = new Blob([base64ToUint8Array(result.base64)], { type: result.mimeType });
  const url = URL.createObjectURL(blob);
  try {
    const anchor = document.createElement('a');
    anchor.href = url;
    anchor.download = result.filename;
    anchor.click();
  } finally {
    // Revoked on a later task: Safari reads the blob asynchronously after the click, and
    // revoking synchronously produces an empty download.
    setTimeout(() => URL.revokeObjectURL(url), 60_000);
  }
}