feat: add print button that replaces page content for printing
Instead of trying to print iframe directly, the print button: 1. Collects all styles from the iframe 2. Replaces the main page content with iframe content 3. Triggers window.print() 4. Reloads the page to restore the app This ensures the PagedJS rendered content prints correctly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
36d3420125
commit
ea74d1891c
1 changed files with 88 additions and 65 deletions
153
src/App.vue
153
src/App.vue
|
|
@ -4,7 +4,7 @@ import EditorPanel from './components/editor/EditorPanel.vue';
|
|||
import ElementPopup from './components/ElementPopup.vue';
|
||||
import PagePopup from './components/PagePopup.vue';
|
||||
import PreviewLoader from './components/PreviewLoader.vue';
|
||||
import { onMounted, onUnmounted, ref, watch, computed, provide } from 'vue';
|
||||
import { onMounted, ref, watch, computed, provide } from 'vue';
|
||||
import { useStylesheetStore } from './stores/stylesheet';
|
||||
import Coloris from '@melloware/coloris';
|
||||
|
||||
|
|
@ -274,79 +274,66 @@ watch(
|
|||
}
|
||||
);
|
||||
|
||||
// Handle Cmd+P / Ctrl+P to print the iframe content
|
||||
const handlePrint = async (event) => {
|
||||
if ((event.metaKey || event.ctrlKey) && event.key === 'p') {
|
||||
event.preventDefault();
|
||||
const frame = activeFrame.value;
|
||||
if (frame && frame.contentDocument) {
|
||||
const doc = frame.contentDocument;
|
||||
// Print the PagedJS content
|
||||
const printPreview = async () => {
|
||||
const frame = activeFrame.value;
|
||||
if (!frame || !frame.contentDocument) return;
|
||||
|
||||
// Collect all styles (inline and from stylesheets)
|
||||
let allStyles = '';
|
||||
const doc = frame.contentDocument;
|
||||
|
||||
// Get inline <style> tags
|
||||
doc.querySelectorAll('style').forEach((style) => {
|
||||
allStyles += style.outerHTML;
|
||||
});
|
||||
// Collect all styles
|
||||
let allStyles = '';
|
||||
|
||||
// Get computed styles from linked stylesheets
|
||||
for (const sheet of doc.styleSheets) {
|
||||
// Get inline <style> tags content
|
||||
doc.querySelectorAll('style').forEach((style) => {
|
||||
allStyles += style.innerHTML + '\n';
|
||||
});
|
||||
|
||||
// Get rules from stylesheets
|
||||
for (const sheet of doc.styleSheets) {
|
||||
try {
|
||||
for (const rule of sheet.cssRules) {
|
||||
allStyles += rule.cssText + '\n';
|
||||
}
|
||||
} catch (e) {
|
||||
// Cross-origin stylesheet, try to fetch it
|
||||
if (sheet.href) {
|
||||
try {
|
||||
for (const rule of sheet.cssRules) {
|
||||
allStyles += rule.cssText + '\n';
|
||||
}
|
||||
} catch (e) {
|
||||
// Cross-origin stylesheet, try to fetch it
|
||||
if (sheet.href) {
|
||||
try {
|
||||
const response = await fetch(sheet.href);
|
||||
const css = await response.text();
|
||||
allStyles += css;
|
||||
} catch (fetchError) {
|
||||
console.warn('Could not fetch stylesheet:', sheet.href);
|
||||
}
|
||||
}
|
||||
const response = await fetch(sheet.href);
|
||||
const css = await response.text();
|
||||
allStyles += css;
|
||||
} catch (fetchError) {
|
||||
console.warn('Could not fetch stylesheet:', sheet.href);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the body content (already rendered by PagedJS)
|
||||
const bodyContent = doc.body.innerHTML;
|
||||
|
||||
// Build the print document
|
||||
const printContent = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>${allStyles}</style>
|
||||
</head>
|
||||
<body>${bodyContent}</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
// Open print window
|
||||
const printWindow = window.open('', '_blank');
|
||||
printWindow.document.write(printContent);
|
||||
printWindow.document.close();
|
||||
|
||||
// Wait for rendering then print
|
||||
setTimeout(() => {
|
||||
printWindow.print();
|
||||
printWindow.close();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
// Save current page content
|
||||
const originalContent = document.body.innerHTML;
|
||||
const originalStyles = document.head.innerHTML;
|
||||
|
||||
// Replace page content with iframe content
|
||||
document.head.innerHTML = `
|
||||
<meta charset="UTF-8">
|
||||
<title>Impression</title>
|
||||
<style>${allStyles}</style>
|
||||
`;
|
||||
document.body.innerHTML = doc.body.innerHTML;
|
||||
|
||||
// Print
|
||||
window.print();
|
||||
|
||||
// Restore original content after print dialog closes
|
||||
setTimeout(() => {
|
||||
document.head.innerHTML = originalStyles;
|
||||
document.body.innerHTML = originalContent;
|
||||
// Re-mount Vue app would be needed, so we reload instead
|
||||
window.location.reload();
|
||||
}, 100);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
renderPreview(true);
|
||||
window.addEventListener('keydown', handlePrint);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('keydown', handlePrint);
|
||||
});
|
||||
onMounted(() => renderPreview(true));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -371,6 +358,12 @@ onUnmounted(() => {
|
|||
|
||||
<ElementPopup ref="elementPopup" :iframeRef="activeFrame" />
|
||||
<PagePopup ref="pagePopup" :iframeRef="activeFrame" @close="handlePagePopupClose" />
|
||||
|
||||
<button class="print-btn" @click="printPreview" title="Imprimer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M17 2H7V6H17V2ZM19 8H5C3.34 8 2 9.34 2 11V17H6V21H18V17H22V11C22 9.34 20.66 8 19 8ZM16 19H8V14H16V19ZM19 12C18.45 12 18 11.55 18 11C18 10.45 18.45 10 19 10C19.55 10 20 10.45 20 11C20 11.55 19.55 12 19 12Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
|
|
@ -403,12 +396,42 @@ onUnmounted(() => {
|
|||
opacity: 0;
|
||||
}
|
||||
|
||||
.print-btn {
|
||||
position: fixed;
|
||||
bottom: 2rem;
|
||||
right: 2rem;
|
||||
width: 3.5rem;
|
||||
height: 3.5rem;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
background: var(--color-page-highlight);
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.print-btn:hover {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.print-btn svg {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
/* Hide UI elements when printing */
|
||||
@media print {
|
||||
#editor-panel,
|
||||
#element-popup,
|
||||
#page-popup,
|
||||
.preview-loader {
|
||||
.preview-loader,
|
||||
.print-btn {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue