163 lines
3.8 KiB
TypeScript
163 lines
3.8 KiB
TypeScript
import { useCallback, useState } from "react";
|
|
|
|
import {
|
|
downloadMarkdownAsDocx,
|
|
downloadMarkdownAsPdf,
|
|
type DocxOptions,
|
|
type PdfOptions,
|
|
} from "./converter";
|
|
|
|
/**
|
|
* Markdown 下载 Hook 配置选项
|
|
*/
|
|
export interface UseMarkdownDownloadOptions {
|
|
/**
|
|
* 下载开始时的回调
|
|
*/
|
|
onDownloadStart?: (format: "docx" | "pdf") => void;
|
|
/**
|
|
* 下载完成时的回调
|
|
*/
|
|
onDownloadEnd?: (format: "docx" | "pdf") => void;
|
|
/**
|
|
* 下载失败时的回调
|
|
*/
|
|
onError?: (error: Error, format: "docx" | "pdf") => void;
|
|
}
|
|
|
|
/**
|
|
* Markdown 下载 Hook 返回值
|
|
*/
|
|
export interface UseMarkdownDownloadReturn {
|
|
/**
|
|
* 当前下载状态
|
|
*/
|
|
isDownloading: "docx" | "pdf" | null;
|
|
/**
|
|
* 下载为 DOCX
|
|
*/
|
|
downloadAsDocx: (
|
|
markdown: string,
|
|
filename: string,
|
|
options?: DocxOptions,
|
|
) => Promise<void>;
|
|
/**
|
|
* 下载为 PDF
|
|
*/
|
|
downloadAsPdf: (
|
|
markdown: string,
|
|
filename: string,
|
|
options?: PdfOptions & {
|
|
resolveAssetUrl?: (
|
|
rawPath: string,
|
|
) => string | null | Promise<string | null>;
|
|
},
|
|
) => Promise<void>;
|
|
/**
|
|
* 是否可以下载(没有正在进行的下载)
|
|
*/
|
|
canDownload: boolean;
|
|
}
|
|
|
|
/**
|
|
* Markdown 文档下载 Hook
|
|
*
|
|
* @description
|
|
* 将 Markdown 内容转换为 DOCX 或 PDF 格式并下载。
|
|
* 可在任何 React + TypeScript 项目中使用。
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* import { useMarkdownDownload } from "./hooks/use-markdown-download";
|
|
*
|
|
* function MyComponent() {
|
|
* const { downloadAsDocx, downloadAsPdf, isDownloading, canDownload } = useMarkdownDownload({
|
|
* onError: (error, format) => {
|
|
* console.error(`Failed to download as ${format}:`, error);
|
|
* },
|
|
* });
|
|
*
|
|
* const handleDownload = () => {
|
|
* downloadAsDocx("# Hello World", "document");
|
|
* };
|
|
*
|
|
* return (
|
|
* <button onClick={handleDownload} disabled={!canDownload}>
|
|
* {isDownloading === "docx" ? "Converting..." : "Download DOCX"}
|
|
* </button>
|
|
* );
|
|
* }
|
|
* ```
|
|
*/
|
|
export function useMarkdownDownload(
|
|
options: UseMarkdownDownloadOptions = {},
|
|
): UseMarkdownDownloadReturn {
|
|
const { onDownloadStart, onDownloadEnd, onError } = options;
|
|
|
|
const [isDownloading, setIsDownloading] = useState<"docx" | "pdf" | null>(
|
|
null,
|
|
);
|
|
|
|
const downloadAsDocx = useCallback(
|
|
async (markdown: string, filename: string, options?: DocxOptions) => {
|
|
if (isDownloading) return;
|
|
|
|
setIsDownloading("docx");
|
|
onDownloadStart?.("docx");
|
|
|
|
try {
|
|
await downloadMarkdownAsDocx(markdown, filename, options);
|
|
} catch (error) {
|
|
onError?.(
|
|
error instanceof Error ? error : new Error(String(error)),
|
|
"docx",
|
|
);
|
|
} finally {
|
|
setIsDownloading(null);
|
|
onDownloadEnd?.("docx");
|
|
}
|
|
},
|
|
[isDownloading, onDownloadStart, onDownloadEnd, onError],
|
|
);
|
|
|
|
const downloadAsPdf = useCallback(
|
|
async (
|
|
markdown: string,
|
|
filename: string,
|
|
options?: PdfOptions & {
|
|
resolveAssetUrl?: (
|
|
rawPath: string,
|
|
) => string | null | Promise<string | null>;
|
|
},
|
|
) => {
|
|
if (isDownloading) return;
|
|
|
|
setIsDownloading("pdf");
|
|
onDownloadStart?.("pdf");
|
|
|
|
try {
|
|
await downloadMarkdownAsPdf(markdown, filename, options);
|
|
} catch (error) {
|
|
onError?.(
|
|
error instanceof Error ? error : new Error(String(error)),
|
|
"pdf",
|
|
);
|
|
} finally {
|
|
setIsDownloading(null);
|
|
onDownloadEnd?.("pdf");
|
|
}
|
|
},
|
|
[isDownloading, onDownloadStart, onDownloadEnd, onError],
|
|
);
|
|
|
|
return {
|
|
isDownloading,
|
|
downloadAsDocx,
|
|
downloadAsPdf,
|
|
canDownload: isDownloading === null,
|
|
};
|
|
}
|
|
|
|
// 导出转换函数,供非 React 环境使用
|
|
export { downloadMarkdownAsDocx, downloadMarkdownAsPdf } from "./converter";
|