diff --git a/app.js b/app.js index 92a4ef0..d5fc0ec 100644 --- a/app.js +++ b/app.js @@ -57,7 +57,8 @@ class YotoCoversApp { cardsPerRow: 2, cardsPerColumn: 5, pageMargin: 10, - cardSpacing: 5 + cardSpacing: 5, + cardFit: 0 }; this.templateSettings = { title: '', @@ -70,6 +71,15 @@ class YotoCoversApp { footerFontSize: null }; + this.previewZoom = { + level: 1.0, + min: 0.25, + max: 3.0, + step: 0.25 + }; + + this.isFullscreen = false; + this.cardVariations = []; this.activeCardIndex = -1; // Currently selected card for editing this.init(); @@ -103,6 +113,11 @@ class YotoCoversApp { this.generatePreview(); }); + document.getElementById('global-card-fit').addEventListener('change', (e) => { + this.printLayout.cardFit = parseFloat(e.target.value); + this.generatePreview(); + }); + // Template settings - REMOVED: Now per-card only // document.getElementById('template-select').addEventListener('change', (e) => { // this.currentTemplate = e.target.value; @@ -181,6 +196,36 @@ class YotoCoversApp { document.getElementById('open-preview').addEventListener('click', () => this.openPreviewInNewTab()); document.getElementById('print-preview').addEventListener('click', () => this.printPreview()); + // Preview zoom and fullscreen + document.getElementById('zoom-in').addEventListener('click', () => this.zoomIn()); + document.getElementById('zoom-out').addEventListener('click', () => this.zoomOut()); + document.getElementById('zoom-reset').addEventListener('click', () => this.resetZoom()); + document.getElementById('fullscreen-btn').addEventListener('click', () => this.toggleFullscreen()); + + // ESC key to exit fullscreen + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && this.isFullscreen) { + this.toggleFullscreen(); + } + }); + + // Click on preview container close button area to exit fullscreen + document.querySelector('.preview-container').addEventListener('click', (e) => { + if (this.isFullscreen) { + const rect = e.currentTarget.getBoundingClientRect(); + const closeButtonArea = { + left: rect.right - 200, + right: rect.right - 20, + top: rect.top + 20, + bottom: rect.top + 60 + }; + if (e.clientX >= closeButtonArea.left && e.clientX <= closeButtonArea.right && + e.clientY >= closeButtonArea.top && e.clientY <= closeButtonArea.bottom) { + this.toggleFullscreen(); + } + } + }); + // Card management document.getElementById('add-card').addEventListener('click', () => this.addCardVariation()); document.getElementById('active-card-select').addEventListener('change', (e) => { @@ -637,6 +682,9 @@ class YotoCoversApp { document.getElementById('card-image-panel-rotation').value = variation.imagePanel?.rotation || 0; document.getElementById('card-footer-panel-border').value = variation.footerPanel?.borderWidth || 0; document.getElementById('card-footer-panel-rotation').value = variation.footerPanel?.rotation || 0; + + // Set card fit + document.getElementById('card-fit').value = variation.cardFit !== undefined ? variation.cardFit : ''; // Populate image options const imageSelect = document.getElementById('card-image-select'); @@ -940,6 +988,14 @@ class YotoCoversApp { borderWidth: parseInt(document.getElementById('card-footer-panel-border').value), rotation: parseInt(document.getElementById('card-footer-panel-rotation').value) }; + + // Update card fit + const cardFitValue = document.getElementById('card-fit').value; + if (cardFitValue === '') { + delete variation.cardFit; + } else { + variation.cardFit = parseFloat(cardFitValue); + } this.updateCardList(); this.generatePreview(); @@ -984,6 +1040,7 @@ class YotoCoversApp { // Apply zoom after iframe loads iframe.onload = () => { this.initDragFunctionality(); + this.updateZoom(); }; // Clean up the previous blob URL @@ -1025,6 +1082,7 @@ class YotoCoversApp { let cardFooterFontSize, cardFooterX, cardFooterY, cardFooterDraggable; let cardShowAccent; let cardPanelMode, cardTitlePanel, cardImagePanel, cardFooterPanel; + let cardFit; if (this.cardVariations.length > 0) { // Use variation settings @@ -1059,6 +1117,7 @@ class YotoCoversApp { cardTitlePanel = variation && variation.titlePanel ? variation.titlePanel : { borderWidth: 0, rotation: 0, expand: false }; cardImagePanel = variation && variation.imagePanel ? variation.imagePanel : { borderWidth: 0, rotation: 0 }; cardFooterPanel = variation && variation.footerPanel ? variation.footerPanel : { borderWidth: 0, rotation: 0 }; + cardFit = variation && variation.cardFit !== undefined ? variation.cardFit : this.printLayout.cardFit; } else { // No variations - auto-assign images imageIndex = i < this.images.length ? i : 0; @@ -1091,6 +1150,7 @@ class YotoCoversApp { cardTitlePanel = { borderWidth: 0, rotation: 0, expand: false }; cardImagePanel = { borderWidth: 0, rotation: 0 }; cardFooterPanel = { borderWidth: 0, rotation: 0 }; + cardFit = this.printLayout.cardFit; } const imageUrl = this.images.length > 0 ? this.images[imageIndex].dataUrl : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; @@ -1125,6 +1185,7 @@ class YotoCoversApp { titlePanel: cardTitlePanel, imagePanel: cardImagePanel, footerPanel: cardFooterPanel, + cardFit: cardFit, isVisible: true }); } else { @@ -1155,14 +1216,17 @@ class YotoCoversApp { titlePanel: { borderWidth: 0, rotation: 0, expand: false }, imagePanel: { borderWidth: 0, rotation: 0 }, footerPanel: { borderWidth: 0, rotation: 0 }, + cardFit: this.printLayout.cardFit, isVisible: false }); } } // Fixed card dimensions: 54mm x 85.6mm - const cardWidthMm = 54; - const cardHeightMm = 85.6; + const baseCardWidthMm = 54; + const baseCardHeightMm = 85.6; + const cardWidthMm = baseCardWidthMm; + const cardHeightMm = baseCardHeightMm; // Calculate grid layout const pageWidthMm = 210; // A4 width @@ -1180,23 +1244,33 @@ class YotoCoversApp { const gridCols = this.printLayout.cardsPerRow; const gridRows = this.printLayout.cardsPerColumn; - htmlContent += ` -