hydropd / website /src /lib /csv.ts
rikardsaqe's picture
Deploy HydroPD container (30 usable models, fixed README)
27124e1 verified
Raw
History Blame Contribute Delete
1.5 kB
// Minimal client-side CSV builder + browser download helper.
// Detectability results come from the live backend (with an offline mock
// fallback when it is unreachable); the CSV downloads are real files.
export type CsvValue = string | number | boolean | null | undefined
function escapeCell(value: unknown): string {
if (value === null || value === undefined) return ''
const s = String(value)
if (/[",\n]/.test(s)) {
return '"' + s.replace(/"/g, '""') + '"'
}
return s
}
/** Build a CSV string from an array of row objects, using `columns` order. */
export function toCSV<T>(
rows: T[],
columns?: { key: keyof T; header: string }[],
): string {
if (rows.length === 0 && !columns) return ''
const cols =
columns ??
(Object.keys(rows[0] as object) as (keyof T)[]).map((k) => ({
key: k,
header: String(k),
}))
const head = cols.map((c) => escapeCell(c.header)).join(',')
const body = rows
.map((row) => cols.map((c) => escapeCell(row[c.key])).join(','))
.join('\n')
return head + '\n' + body
}
/** Trigger a browser download of `text` as `filename`. */
export function download(
filename: string,
text: string,
mime = 'text/csv;charset=utf-8',
): void {
const blob = new Blob([text], { type: mime })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = filename
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
}