173 lines
5.7 KiB
TypeScript
173 lines
5.7 KiB
TypeScript
// app/components/MarkdownModal.tsx
|
|
|
|
import { X, Download } from "lucide-react";
|
|
import { jsPDF } from "jspdf";
|
|
import { useState } from "react";
|
|
import { type PageObject } from "../page"; // Importer le type depuis la page principale
|
|
|
|
interface HtmlModalProps {
|
|
content: PageObject[] | null; // Utilise le type PageObject importé
|
|
onClose: () => void;
|
|
}
|
|
|
|
// Moteur de rendu HTML vers PDF, robuste et récursif
|
|
const renderHtmlOnPdfPage = (pdf: jsPDF, htmlContent: string) => {
|
|
const pageHeight = pdf.internal.pageSize.getHeight();
|
|
const pageWidth = pdf.internal.pageSize.getWidth();
|
|
const margin = 15;
|
|
let cursorY = margin;
|
|
|
|
const container = document.createElement("div");
|
|
container.innerHTML = htmlContent;
|
|
|
|
const processNode = (
|
|
node: ChildNode,
|
|
currentStyle: { fontStyle: "normal" | "bold" | "italic"; fontSize: number }
|
|
) => {
|
|
if (cursorY > pageHeight - margin) {
|
|
pdf.addPage();
|
|
cursorY = margin;
|
|
}
|
|
|
|
const nextStyle = { ...currentStyle };
|
|
let spacingAfter = 0;
|
|
|
|
switch (node.nodeName) {
|
|
case "H1":
|
|
nextStyle.fontSize = 22;
|
|
nextStyle.fontStyle = "bold";
|
|
spacingAfter = 8;
|
|
break;
|
|
case "H2":
|
|
nextStyle.fontSize = 18;
|
|
nextStyle.fontStyle = "bold";
|
|
spacingAfter = 6;
|
|
break;
|
|
case "H3":
|
|
nextStyle.fontSize = 14;
|
|
nextStyle.fontStyle = "bold";
|
|
spacingAfter = 4;
|
|
break;
|
|
case "P":
|
|
nextStyle.fontSize = 10;
|
|
nextStyle.fontStyle = "normal";
|
|
spacingAfter = 4;
|
|
break;
|
|
case "UL":
|
|
spacingAfter = 4;
|
|
break;
|
|
case "STRONG":
|
|
case "B":
|
|
nextStyle.fontStyle = "bold";
|
|
break;
|
|
}
|
|
|
|
if (node.nodeType === Node.TEXT_NODE && node.textContent?.trim()) {
|
|
pdf.setFontSize(nextStyle.fontSize);
|
|
pdf.setFont("Helvetica", nextStyle.fontStyle);
|
|
const textLines = node.textContent.split("\n");
|
|
textLines.forEach((line) => {
|
|
if (line.trim() === "") {
|
|
cursorY += nextStyle.fontSize * 0.4;
|
|
return;
|
|
}
|
|
const splitText = pdf.splitTextToSize(line, pageWidth - margin * 2);
|
|
pdf.text(splitText, margin, cursorY);
|
|
cursorY += splitText.length * nextStyle.fontSize * 0.4;
|
|
});
|
|
} else if (node.nodeName === "UL") {
|
|
Array.from((node as HTMLUListElement).children).forEach((li) => {
|
|
const liText = `• ${li.textContent?.trim() || ""}`;
|
|
const lines = pdf.splitTextToSize(liText, pageWidth - margin * 2 - 5);
|
|
pdf.setFontSize(10);
|
|
pdf.setFont("Helvetica", "normal");
|
|
pdf.text(lines, margin + 5, cursorY);
|
|
cursorY += lines.length * 10 * 0.4 + 2;
|
|
});
|
|
}
|
|
|
|
if (node.childNodes.length > 0) {
|
|
node.childNodes.forEach((child) => processNode(child, nextStyle));
|
|
}
|
|
cursorY += spacingAfter;
|
|
};
|
|
|
|
container.childNodes.forEach((node) =>
|
|
processNode(node, { fontStyle: "normal", fontSize: 10 })
|
|
);
|
|
};
|
|
|
|
export default function MarkdownModal({ content, onClose }: HtmlModalProps) {
|
|
const [isDownloading, setIsDownloading] = useState(false);
|
|
if (!content) return null;
|
|
|
|
const handleDownloadPdf = async () => {
|
|
if (!content || content.length === 0) return;
|
|
setIsDownloading(true);
|
|
try {
|
|
const pdf = new jsPDF({ orientation: "p", unit: "mm", format: "a4" });
|
|
pdf.setFont("Helvetica", "normal");
|
|
|
|
// Mis à jour pour boucler sur les objets et passer le contenu HTML
|
|
content.forEach((page, index) => {
|
|
if (index > 0) pdf.addPage();
|
|
renderHtmlOnPdfPage(pdf, page.htmlContent);
|
|
});
|
|
|
|
pdf.save("document_anonymise.pdf");
|
|
} catch (error) {
|
|
console.error("Erreur lors de la génération du PDF:", error);
|
|
} finally {
|
|
setIsDownloading(false);
|
|
}
|
|
};
|
|
|
|
// Mis à jour pour extraire le contenu HTML de chaque page avant de l'afficher
|
|
const previewHtml = content
|
|
.map((page) => page.htmlContent)
|
|
.join(
|
|
'<hr style="margin: 2.5rem 0; border-style: dashed; border-color: rgba(255,255,255,0.2);">'
|
|
);
|
|
|
|
return (
|
|
<div
|
|
className="fixed inset-0 bg-black bg-opacity-70 z-50 flex justify-center items-center p-4"
|
|
onClick={onClose}
|
|
>
|
|
<div
|
|
className="bg-[#061717] border-4 border-white shadow-[10px_10px_0_0_black] w-full max-w-3xl h-[80vh] flex flex-col relative"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<header className="flex items-center justify-between p-4 border-b-4 border-white">
|
|
<h3 className="text-lg font-black text-white uppercase">
|
|
Aperçu du Document
|
|
</h3>
|
|
<div className="flex gap-2">
|
|
<button
|
|
onClick={handleDownloadPdf}
|
|
disabled={isDownloading}
|
|
className="p-2 bg-[#F7AB6E] text-white border-2 border-white shadow-[3px_3px_0_0_black] hover:shadow-[1px_1px_0_0_black] active:shadow-none active:translate-x-0.5 active:translate-y-0.5 disabled:opacity-50"
|
|
>
|
|
{isDownloading ? (
|
|
<div className="h-5 w-5 animate-spin rounded-full border-2 border-white border-t-transparent" />
|
|
) : (
|
|
<Download className="h-5 w-5" />
|
|
)}
|
|
</button>
|
|
<button
|
|
onClick={onClose}
|
|
className="p-2 bg-[#F7AB6E] text-white border-2 border-white shadow-[3px_3px_0_0_black] hover:shadow-[1px_1px_0_0_black] active:shadow-none active:translate-x-0.5 active:translate-y-0.5"
|
|
>
|
|
<X className="h-5 w-5" />
|
|
</button>
|
|
</div>
|
|
</header>
|
|
<div
|
|
className="preview-content p-6 overflow-y-auto w-full h-full"
|
|
dangerouslySetInnerHTML={{ __html: previewHtml }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|