export
This commit is contained in:
41
src/utils/csv.js
Normal file
41
src/utils/csv.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/** Escape a value for a CSV field ("..." double-quoting per RFC 4180). */
|
||||
function csvField(value) {
|
||||
const text = value == null ? '' : String(value);
|
||||
return /[";\n\r]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a CSV string from an array of row objects (keys become the header).
|
||||
* Uses `;` as the field separator with a `sep=;` hint line so Excel opens it
|
||||
* correctly regardless of the OS list-separator locale.
|
||||
*/
|
||||
export function toCsv(rows) {
|
||||
if (!rows.length) return '';
|
||||
const headers = Object.keys(rows[0]);
|
||||
const lines = [
|
||||
'sep=;',
|
||||
headers.map(csvField).join(';'),
|
||||
...rows.map((row) => headers.map((h) => csvField(row[h])).join(';')),
|
||||
];
|
||||
return lines.join('\r\n');
|
||||
}
|
||||
|
||||
/** Trigger a browser download of the given string, encoded as Windows-1252. */
|
||||
export function downloadCsv(filename, csv) {
|
||||
const bytes = new Uint8Array(csv.length);
|
||||
for (let i = 0; i < csv.length; i++) {
|
||||
const code = csv.charCodeAt(i);
|
||||
// Windows-1252 covers the single-byte Latin-1 range + punctuation (e.g. „ " –).
|
||||
// Category names are German/single-byte, so chars are well inside this range.
|
||||
bytes[i] = code <= 0xff ? code : 0x3f; // '?' for any out-of-range char
|
||||
}
|
||||
const blob = new Blob([bytes], { type: 'text/csv;charset=windows-1252;' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
Reference in New Issue
Block a user