init
This commit is contained in:
712
app.js
Normal file
712
app.js
Normal file
@@ -0,0 +1,712 @@
|
||||
// Global variables
|
||||
let quill;
|
||||
let currentMode = 'richtext';
|
||||
let currentContent = '';
|
||||
let pages = [];
|
||||
|
||||
// Initialize Quill editor
|
||||
function initQuill() {
|
||||
quill = new Quill('#quillEditor', {
|
||||
theme: 'snow',
|
||||
modules: {
|
||||
toolbar: '#quillToolbar'
|
||||
},
|
||||
placeholder: 'Start writing your letter content here...'
|
||||
});
|
||||
|
||||
// Update preview on content change
|
||||
quill.on('text-change', () => {
|
||||
updatePreview();
|
||||
});
|
||||
}
|
||||
|
||||
// Mode switching
|
||||
document.getElementById('richTextMode').addEventListener('click', () => {
|
||||
switchMode('richtext');
|
||||
});
|
||||
|
||||
document.getElementById('markdownMode').addEventListener('click', () => {
|
||||
switchMode('markdown');
|
||||
});
|
||||
|
||||
document.getElementById('previewMode').addEventListener('click', () => {
|
||||
switchMode('preview');
|
||||
});
|
||||
|
||||
function switchMode(mode) {
|
||||
// Update button states
|
||||
document.querySelectorAll('.toolbar-left .btn').forEach(btn => {
|
||||
btn.classList.remove('active', 'btn-primary');
|
||||
});
|
||||
|
||||
// Hide all editors
|
||||
document.querySelectorAll('.editor-mode').forEach(editor => {
|
||||
editor.classList.remove('active');
|
||||
});
|
||||
|
||||
currentMode = mode;
|
||||
|
||||
switch(mode) {
|
||||
case 'richtext':
|
||||
document.getElementById('richTextMode').classList.add('active', 'btn-primary');
|
||||
document.getElementById('richTextEditor').classList.add('active');
|
||||
// Convert markdown to rich text if switching from markdown
|
||||
if (currentMode === 'markdown') {
|
||||
const markdownContent = document.getElementById('markdownTextarea').value;
|
||||
const html = marked.parse(markdownContent);
|
||||
quill.root.innerHTML = DOMPurify.sanitize(html);
|
||||
}
|
||||
break;
|
||||
case 'markdown':
|
||||
document.getElementById('markdownMode').classList.add('active', 'btn-primary');
|
||||
document.getElementById('markdownEditor').classList.add('active');
|
||||
// Convert rich text to markdown if switching from rich text
|
||||
if (currentMode === 'richtext') {
|
||||
const html = quill.root.innerHTML;
|
||||
document.getElementById('markdownTextarea').value = htmlToMarkdown(html);
|
||||
}
|
||||
break;
|
||||
case 'preview':
|
||||
document.getElementById('previewMode').classList.add('active', 'btn-primary');
|
||||
// Hide editor section
|
||||
document.querySelector('.editor-section').style.display = 'none';
|
||||
document.querySelector('.preview-section').style.flex = '1';
|
||||
break;
|
||||
}
|
||||
|
||||
if (mode !== 'preview') {
|
||||
document.querySelector('.editor-section').style.display = 'flex';
|
||||
document.querySelector('.preview-section').style.flex = '1';
|
||||
}
|
||||
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
// Markdown editor functions
|
||||
function insertMarkdown(before, after) {
|
||||
const textarea = document.getElementById('markdownTextarea');
|
||||
const start = textarea.selectionStart;
|
||||
const end = textarea.selectionEnd;
|
||||
const selectedText = textarea.value.substring(start, end);
|
||||
const replacement = before + selectedText + after;
|
||||
|
||||
textarea.value = textarea.value.substring(0, start) + replacement + textarea.value.substring(end);
|
||||
textarea.focus();
|
||||
textarea.setSelectionRange(start + before.length, start + before.length + selectedText.length);
|
||||
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
function insertTable() {
|
||||
const table = '\n| Header 1 | Header 2 | Header 3 |\n|----------|----------|----------|\n| Cell 1 | Cell 2 | Cell 3 |\n| Cell 4 | Cell 5 | Cell 6 |\n';
|
||||
const textarea = document.getElementById('markdownTextarea');
|
||||
const start = textarea.selectionStart;
|
||||
|
||||
textarea.value = textarea.value.substring(0, start) + table + textarea.value.substring(start);
|
||||
textarea.focus();
|
||||
textarea.setSelectionRange(start + table.length, start + table.length);
|
||||
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
// Update preview on markdown change
|
||||
document.getElementById('markdownTextarea').addEventListener('input', updatePreview);
|
||||
|
||||
// Convert HTML to Markdown (basic conversion)
|
||||
function htmlToMarkdown(html) {
|
||||
let markdown = html;
|
||||
|
||||
// Headers
|
||||
markdown = markdown.replace(/<h1[^>]*>(.*?)<\/h1>/gi, '# $1\n');
|
||||
markdown = markdown.replace(/<h2[^>]*>(.*?)<\/h2>/gi, '## $1\n');
|
||||
markdown = markdown.replace(/<h3[^>]*>(.*?)<\/h3>/gi, '### $1\n');
|
||||
|
||||
// Bold and italic
|
||||
markdown = markdown.replace(/<strong[^>]*>(.*?)<\/strong>/gi, '**$1**');
|
||||
markdown = markdown.replace(/<b[^>]*>(.*?)<\/b>/gi, '**$1**');
|
||||
markdown = markdown.replace(/<em[^>]*>(.*?)<\/em>/gi, '*$1*');
|
||||
markdown = markdown.replace(/<i[^>]*>(.*?)<\/i>/gi, '*$1*');
|
||||
|
||||
// Links
|
||||
markdown = markdown.replace(/<a[^>]+href="([^"]*)"[^>]*>(.*?)<\/a>/gi, '[$2]($1)');
|
||||
|
||||
// Images
|
||||
markdown = markdown.replace(/<img[^>]+src="([^"]*)"[^>]*alt="([^"]*)"[^>]*>/gi, '');
|
||||
|
||||
// Lists
|
||||
markdown = markdown.replace(/<ul[^>]*>(.*?)<\/ul>/gis, (match, content) => {
|
||||
return content.replace(/<li[^>]*>(.*?)<\/li>/gi, '- $1\n');
|
||||
});
|
||||
|
||||
markdown = markdown.replace(/<ol[^>]*>(.*?)<\/ol>/gis, (match, content) => {
|
||||
let index = 1;
|
||||
return content.replace(/<li[^>]*>(.*?)<\/li>/gi, () => {
|
||||
return `${index++}. $1\n`;
|
||||
});
|
||||
});
|
||||
|
||||
// Paragraphs
|
||||
markdown = markdown.replace(/<p[^>]*>(.*?)<\/p>/gi, '$1\n\n');
|
||||
|
||||
// Line breaks
|
||||
markdown = markdown.replace(/<br[^>]*>/gi, '\n');
|
||||
|
||||
// Remove remaining HTML tags
|
||||
markdown = markdown.replace(/<[^>]*>/g, '');
|
||||
|
||||
// Clean up extra whitespace
|
||||
markdown = markdown.replace(/\n\n+/g, '\n\n').trim();
|
||||
|
||||
return markdown;
|
||||
}
|
||||
|
||||
// Update preview function
|
||||
function updatePreview() {
|
||||
let content = '';
|
||||
|
||||
if (currentMode === 'richtext') {
|
||||
content = quill.root.innerHTML;
|
||||
} else if (currentMode === 'markdown') {
|
||||
const markdownContent = document.getElementById('markdownTextarea').value;
|
||||
content = marked.parse(markdownContent);
|
||||
}
|
||||
|
||||
currentContent = DOMPurify.sanitize(content);
|
||||
paginateContent();
|
||||
}
|
||||
|
||||
// Paginate content
|
||||
function paginateContent() {
|
||||
const container = document.getElementById('previewContainer');
|
||||
container.innerHTML = '';
|
||||
|
||||
// Create a temporary element to measure content
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.style.cssText = 'position: absolute; visibility: hidden; width: 130mm; font-size: 16px; line-height: 1.6;';
|
||||
tempDiv.innerHTML = currentContent;
|
||||
document.body.appendChild(tempDiv);
|
||||
|
||||
// Split content into pages
|
||||
const maxHeight = 200; // mm - approximate content area height
|
||||
const elements = Array.from(tempDiv.children);
|
||||
pages = [];
|
||||
let currentPage = [];
|
||||
let currentHeight = 0;
|
||||
|
||||
elements.forEach(element => {
|
||||
const elementHeight = element.offsetHeight / 3.78; // Convert px to mm (approximate)
|
||||
|
||||
if (currentHeight + elementHeight > maxHeight && currentPage.length > 0) {
|
||||
pages.push(currentPage);
|
||||
currentPage = [];
|
||||
currentHeight = 0;
|
||||
}
|
||||
|
||||
currentPage.push(element.outerHTML);
|
||||
currentHeight += elementHeight;
|
||||
});
|
||||
|
||||
if (currentPage.length > 0) {
|
||||
pages.push(currentPage);
|
||||
}
|
||||
|
||||
// If no pages created, create one with all content
|
||||
if (pages.length === 0) {
|
||||
pages.push([currentContent]);
|
||||
}
|
||||
|
||||
document.body.removeChild(tempDiv);
|
||||
|
||||
// Create preview pages
|
||||
pages.forEach((pageContent, index) => {
|
||||
const pageDiv = createPreviewPage(pageContent.join(''), index + 1);
|
||||
container.appendChild(pageDiv);
|
||||
});
|
||||
|
||||
// Update page info
|
||||
document.getElementById('currentPage').textContent = '1';
|
||||
document.getElementById('totalPages').textContent = pages.length;
|
||||
}
|
||||
|
||||
// Create preview page
|
||||
function createPreviewPage(content, pageNumber) {
|
||||
const pageDiv = document.createElement('div');
|
||||
pageDiv.className = 'preview-page';
|
||||
pageDiv.innerHTML = `
|
||||
<!-- Background Logo -->
|
||||
<img src="logo.png" alt="OmegaTech Solution Logo" class="preview-watermark">
|
||||
|
||||
<!-- Header Section -->
|
||||
<div class="preview-header-section">
|
||||
<img src="logo.png" alt="OmegaTech Solution" class="preview-logo">
|
||||
<div class="preview-company-info">
|
||||
<h1 class="preview-company-name">OMEGATECH SOLUTION</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content Area -->
|
||||
<div class="preview-content-area">
|
||||
${content}
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="preview-footer">
|
||||
<div class="preview-footer-content">
|
||||
<div class="footer-left">
|
||||
<div class="footer-item">
|
||||
<svg class="footer-icon" viewBox="0 0 24 24">
|
||||
<path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/>
|
||||
</svg>
|
||||
hello@omegatechsolution.org
|
||||
</div>
|
||||
<div class="footer-item">
|
||||
<svg class="footer-icon" viewBox="0 0 24 24">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.94-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/>
|
||||
</svg>
|
||||
https://omegatechsolution.org
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-right">
|
||||
<div class="footer-item">
|
||||
<svg class="footer-icon" viewBox="0 0 24 24">
|
||||
<path d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z"/>
|
||||
</svg>
|
||||
+960 9198026
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return pageDiv;
|
||||
}
|
||||
|
||||
// PDF Quality Settings
|
||||
const pdfQualitySettings = {
|
||||
low: { scale: 0.8, quality: 0.7, compression: 'FAST' },
|
||||
medium: { scale: 1.2, quality: 0.85, compression: 'FAST' },
|
||||
high: { scale: 1.5, quality: 0.95, compression: 'SLOW' }
|
||||
};
|
||||
|
||||
// Dropdown menu functionality
|
||||
document.getElementById('exportPdfBtn').addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const menu = document.getElementById('pdfQualityMenu');
|
||||
menu.classList.toggle('show');
|
||||
});
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
document.addEventListener('click', () => {
|
||||
document.getElementById('pdfQualityMenu').classList.remove('show');
|
||||
});
|
||||
|
||||
// Handle PDF export with quality selection
|
||||
document.querySelectorAll('.dropdown-item').forEach(item => {
|
||||
item.addEventListener('click', async (e) => {
|
||||
const quality = e.target.getAttribute('data-quality');
|
||||
document.getElementById('pdfQualityMenu').classList.remove('show');
|
||||
await exportPDF(quality);
|
||||
});
|
||||
});
|
||||
|
||||
// Export to PDF function
|
||||
async function exportPDF(quality = 'medium') {
|
||||
const { jsPDF } = window.jspdf;
|
||||
const settings = pdfQualitySettings[quality];
|
||||
|
||||
// Show export modal
|
||||
const modal = createExportModal(`Generating ${quality} quality PDF...`);
|
||||
document.body.appendChild(modal);
|
||||
|
||||
try {
|
||||
const pdf = new jsPDF('p', 'mm', 'a4', true); // Enable compression
|
||||
const pageWidth = pdf.internal.pageSize.getWidth();
|
||||
const pageHeight = pdf.internal.pageSize.getHeight();
|
||||
|
||||
for (let i = 0; i < pages.length; i++) {
|
||||
// Update progress
|
||||
updateExportProgress(modal, (i / pages.length) * 100);
|
||||
|
||||
// Create a temporary page for rendering
|
||||
const tempPage = createPreviewPage(pages[i].join(''), i + 1);
|
||||
tempPage.style.cssText = 'position: absolute; left: -9999px; width: 210mm; height: 297mm; background: white;';
|
||||
document.body.appendChild(tempPage);
|
||||
|
||||
// Wait for images to load
|
||||
await waitForImages(tempPage);
|
||||
|
||||
// Render to canvas with quality-based settings
|
||||
const canvas = await html2canvas(tempPage, {
|
||||
scale: settings.scale,
|
||||
useCORS: true,
|
||||
backgroundColor: '#ffffff',
|
||||
logging: false,
|
||||
imageTimeout: 15000,
|
||||
removeContainer: true,
|
||||
allowTaint: false,
|
||||
foreignObjectRendering: false
|
||||
});
|
||||
|
||||
// Convert to JPEG with quality-based compression
|
||||
const imgData = canvas.toDataURL('image/jpeg', settings.quality);
|
||||
if (i > 0) {
|
||||
pdf.addPage();
|
||||
}
|
||||
|
||||
// Add image with compression
|
||||
pdf.addImage(imgData, 'JPEG', 0, 0, pageWidth, pageHeight, undefined, settings.compression);
|
||||
|
||||
// Clean up
|
||||
document.body.removeChild(tempPage);
|
||||
|
||||
// Clear canvas to free memory
|
||||
canvas.width = 0;
|
||||
canvas.height = 0;
|
||||
}
|
||||
|
||||
// Update progress to 100%
|
||||
updateExportProgress(modal, 100);
|
||||
|
||||
// Save PDF with compression
|
||||
const pdfOutput = pdf.output('blob');
|
||||
const url = URL.createObjectURL(pdfOutput);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `letter-omegatech-${quality}.pdf`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
// Close modal after a short delay
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(modal);
|
||||
}, 500);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error generating PDF:', error);
|
||||
alert('Error generating PDF. Please try again.');
|
||||
document.body.removeChild(modal);
|
||||
}
|
||||
}
|
||||
|
||||
// Export to HTML
|
||||
document.getElementById('exportHtml').addEventListener('click', () => {
|
||||
// Create HTML document
|
||||
let htmlContent = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>OmegaTech Solution - Letter</title>
|
||||
<style>
|
||||
${getExportStyles()}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="letterhead-container">`;
|
||||
|
||||
// Add each page
|
||||
pages.forEach((pageContent, index) => {
|
||||
htmlContent += `
|
||||
<div class="page">
|
||||
<!-- Background Logo -->
|
||||
<img src="logo.png" alt="OmegaTech Solution Logo" class="company-logo-bg">
|
||||
|
||||
<!-- Header Section -->
|
||||
<div class="header">
|
||||
<img src="logo.png" alt="OmegaTech Solution" class="header-logo">
|
||||
<div class="company-info">
|
||||
<h1 class="company-name">OMEGATECH SOLUTION</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content Area -->
|
||||
<div class="content-area">
|
||||
${pageContent.join('')}
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="footer">
|
||||
<div class="footer-content">
|
||||
<div class="footer-left">
|
||||
<div class="footer-item">
|
||||
<svg class="footer-icon" viewBox="0 0 24 24">
|
||||
<path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/>
|
||||
</svg>
|
||||
hello@omegatechsolution.org
|
||||
</div>
|
||||
<div class="footer-item">
|
||||
<svg class="footer-icon" viewBox="0 0 24 24">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.94-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/>
|
||||
</svg>
|
||||
https://omegatechsolution.org
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-right">
|
||||
<div class="footer-item">
|
||||
<svg class="footer-icon" viewBox="0 0 24 24">
|
||||
<path d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z"/>
|
||||
</svg>
|
||||
+960 9198026
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
htmlContent += `
|
||||
</div>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
// Download HTML file
|
||||
const blob = new Blob([htmlContent], { type: 'text/html' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'letter-omegatech.html';
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
});
|
||||
|
||||
// Helper functions
|
||||
function createExportModal(message) {
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'export-modal';
|
||||
modal.innerHTML = `
|
||||
<div class="export-modal-content">
|
||||
<h3>${message}</h3>
|
||||
<div class="export-progress">
|
||||
<div class="export-progress-bar" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
return modal;
|
||||
}
|
||||
|
||||
function updateExportProgress(modal, percent) {
|
||||
const progressBar = modal.querySelector('.export-progress-bar');
|
||||
progressBar.style.width = percent + '%';
|
||||
}
|
||||
|
||||
function waitForImages(element) {
|
||||
const images = element.querySelectorAll('img');
|
||||
const promises = Array.from(images).map(img => {
|
||||
return new Promise((resolve) => {
|
||||
if (img.complete) {
|
||||
resolve();
|
||||
} else {
|
||||
img.onload = resolve;
|
||||
img.onerror = resolve;
|
||||
}
|
||||
});
|
||||
});
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
function getExportStyles() {
|
||||
return `
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #f8f9fa;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.letterhead-container {
|
||||
width: 210mm;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
background: white;
|
||||
box-shadow: 0 0 20px rgba(0,0,0,0.1);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.page {
|
||||
width: 210mm;
|
||||
height: 297mm;
|
||||
padding: 40px;
|
||||
box-sizing: border-box;
|
||||
page-break-after: always;
|
||||
position: relative;
|
||||
background: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.page:last-child {
|
||||
page-break-after: auto;
|
||||
}
|
||||
|
||||
.company-logo-bg {
|
||||
max-width: 600px;
|
||||
opacity: 0.08;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 40px;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.header-logo {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
margin-right: 20px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.company-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.company-name {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #2c3e50;
|
||||
margin: 0;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.content-area {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content-area h1 {
|
||||
font-size: 28px;
|
||||
margin: 20px 0 15px 0;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.content-area h2 {
|
||||
font-size: 22px;
|
||||
margin: 18px 0 12px 0;
|
||||
color: #34495e;
|
||||
}
|
||||
|
||||
.content-area h3 {
|
||||
font-size: 18px;
|
||||
margin: 15px 0 10px 0;
|
||||
color: #34495e;
|
||||
}
|
||||
|
||||
.content-area p {
|
||||
margin: 10px 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.content-area ul, .content-area ol {
|
||||
margin: 10px 0;
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
.content-area img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.content-area table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.content-area th, .content-area td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.content-area th {
|
||||
background-color: #f8f8f8;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: auto;
|
||||
padding-top: 25px;
|
||||
border-top: 1px solid #ecf0f1;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.footer-left, .footer-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.footer-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.footer-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 8px;
|
||||
fill: #666;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
background: white;
|
||||
}
|
||||
|
||||
.letterhead-container {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.page {
|
||||
page-break-after: always;
|
||||
margin: 0;
|
||||
padding: 20mm;
|
||||
}
|
||||
|
||||
.page:last-child {
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.company-logo-bg {
|
||||
max-width: 500px;
|
||||
opacity: 0.05;
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
// Initialize on page load
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initQuill();
|
||||
updatePreview();
|
||||
});
|
||||
Reference in New Issue
Block a user