import type { Ref } from 'react' import type { DownloadItem } from './types' import html2canvas from 'html2canvas' import { ImageOutlined } from '@mui/icons-material' import { SvgIcon } from '@mui/material' // Helper to escape CSV cell values function escapeCSVCell(value: D): string { const str = value == null ? '' : typeof value === 'object' ? JSON.stringify(value) : String(value) // If the value contains a comma, quote, or newline, wrap in quotes and escape quotes if (/[",\n]/.test(str)) { return `"${str.replace(/"/g, '""')}"` } return str } async function downloadFileToCSV(data: D[][]) { const csvContent = data .map((row) => row.map(escapeCSVCell).join(',')) .join('\n') const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }) return Promise.resolve(URL.createObjectURL(blob)) } /** * Pre-configured download item for exporting widget data as a CSV file. * * Converts a 2D array of data into CSV format with proper escaping and * triggers a browser download. Revokes the object URL after download. */ export const downloadToCSV: DownloadItem = { id: 'csv', label: 'CSV', icon: ( ), modifier: downloadFileToCSV, callback: (data) => { URL.revokeObjectURL(data) }, } async function downloadFileToPNG(ref: Ref | undefined) { if (!ref || typeof ref === 'function' || !ref.current) { // eslint-disable-next-line no-console console.warn( '[CARTO downloadFileToPNG] Invalid ref passed to downloadFileToPNG. ' + 'Expected a React ref object with a .current property pointing to an HTMLElement. ' + 'Download aborted.', ) return } const element = ref.current const clone = element.cloneNode(true) as HTMLElement clone.querySelector('.widget-toolbar-container')?.remove() clone.querySelector('.widget-wrapper-actions')?.remove() document.body.appendChild(clone) const rect = clone.getBoundingClientRect() const opts = { useCORS: true, scale: 2, backgroundColor: '#fff', width: rect.width, height: rect.height, } const canvasResult: HTMLCanvasElement = await html2canvas(clone, opts) const result = canvasResult.toDataURL('image/png') document.body.removeChild(clone) return Promise.resolve(result) } /** * Pre-configured download item for exporting a widget as a PNG image. * * Uses html2canvas to capture the widget DOM element referenced by a React ref. * Strips toolbar and action elements before capturing. * * @remarks * The modifier expects a React ref to the widget's root HTML element, not raw data. */ export const downloadToPNG: Omit & { modifier: ( ref: Ref | undefined, ) => Promise } = { id: 'png', label: 'PNG', icon: , modifier: downloadFileToPNG, }