mirror of
https://github.com/vercel/streamdown.git
synced 2026-09-19 02:18:37 +08:00
8b1c262982
* fix(save): prepend UTF-8 BOM to CSV blobs for Excel compatibility Closes #440 * fix(download): refactor TableDownloadButton to use save() for CSV downloads and ensure UTF-8 BOM compatibility
49 lines
1.8 KiB
TypeScript
49 lines
1.8 KiB
TypeScript
import { type ClassValue, clsx } from 'clsx';
|
|
import { twMerge } from 'tailwind-merge';
|
|
|
|
export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs));
|
|
|
|
export type CnFunction = (...inputs: ClassValue[]) => string;
|
|
|
|
/**
|
|
* Prepends a prefix to each Tailwind utility class in a class string.
|
|
* Used to support Tailwind v4's `prefix()` feature.
|
|
*
|
|
* @example
|
|
* prefixClasses("tw", "flex items-center") // "tw:flex tw:items-center"
|
|
* prefixClasses("tw", "dark:bg-red-500") // "tw:dark:bg-red-500"
|
|
*/
|
|
export const prefixClasses = (prefix: string, classString: string): string => {
|
|
if (!prefix || !classString) return classString;
|
|
const prefixWithColon = `${prefix}:`;
|
|
return classString
|
|
.split(/\s+/)
|
|
.filter(Boolean)
|
|
.map((cls) => cls.startsWith(prefixWithColon) ? cls : `${prefix}:${cls}`)
|
|
.join(" ");
|
|
};
|
|
|
|
/**
|
|
* Creates a prefix-aware `cn` function. When no prefix is provided,
|
|
* returns the standard `cn` with zero overhead.
|
|
*/
|
|
export const createCn = (prefix?: string): CnFunction => {
|
|
if (!prefix) return cn;
|
|
return (...inputs: ClassValue[]) => prefixClasses(prefix, twMerge(clsx(inputs)));
|
|
};
|
|
|
|
export const save = (filename: string, content: string | Blob, mimeType: string) => {
|
|
// Prepend UTF-8 BOM for CSV so Excel on Windows correctly detects the encoding.
|
|
// Without it, Excel falls back to the system ANSI codepage and corrupts non-ASCII text.
|
|
const bom = typeof content === 'string' && mimeType.startsWith('text/csv') ? '\uFEFF' : '';
|
|
const blob = typeof content === 'string' ? new Blob([bom + content], { type: mimeType }) : content;
|
|
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);
|
|
};
|