feat: add panel settings and footer customization options in the editor
This commit is contained in:
@@ -36,7 +36,11 @@ class YotoCoversApp {
|
||||
fontFamily: 'Comic Sans MS',
|
||||
fontWeight: 'bold',
|
||||
footerColor: '#ff6b6b',
|
||||
fullBleed: false
|
||||
fullBleed: false,
|
||||
panelMode: true,
|
||||
titlePanel: { borderWidth: 2, rotation: -2, expand: true },
|
||||
imagePanel: { borderWidth: 3, rotation: 1 },
|
||||
footerPanel: { borderWidth: 2, rotation: 3 }
|
||||
},
|
||||
polaroid: {
|
||||
backgroundStyle: 'polaroid',
|
||||
@@ -235,10 +239,27 @@ class YotoCoversApp {
|
||||
document.getElementById('card-title-x').addEventListener('input', () => this.updateActiveCard());
|
||||
document.getElementById('card-title-y').addEventListener('input', () => this.updateActiveCard());
|
||||
document.getElementById('card-title-draggable').addEventListener('change', () => this.updateActiveCard());
|
||||
document.getElementById('card-footer-font-size').addEventListener('input', () => this.updateActiveCard());
|
||||
document.getElementById('card-footer-x').addEventListener('input', () => this.updateActiveCard());
|
||||
document.getElementById('card-footer-y').addEventListener('input', () => this.updateActiveCard());
|
||||
document.getElementById('card-footer-draggable').addEventListener('change', () => this.updateActiveCard());
|
||||
document.getElementById('card-image-fit').addEventListener('change', () => this.updateActiveCard());
|
||||
document.getElementById('card-image-position').addEventListener('change', () => this.updateActiveCard());
|
||||
document.getElementById('card-image-select').addEventListener('change', () => this.updateActiveCard());
|
||||
|
||||
// Panel settings
|
||||
document.getElementById('card-panel-mode').addEventListener('change', () => {
|
||||
this.updateActiveCard();
|
||||
this.updateSectionVisibility();
|
||||
});
|
||||
document.getElementById('card-title-panel-border').addEventListener('input', () => this.updateActiveCard());
|
||||
document.getElementById('card-title-panel-rotation').addEventListener('input', () => this.updateActiveCard());
|
||||
document.getElementById('card-title-panel-expand').addEventListener('change', () => this.updateActiveCard());
|
||||
document.getElementById('card-image-panel-border').addEventListener('input', () => this.updateActiveCard());
|
||||
document.getElementById('card-image-panel-rotation').addEventListener('input', () => this.updateActiveCard());
|
||||
document.getElementById('card-footer-panel-border').addEventListener('input', () => this.updateActiveCard());
|
||||
document.getElementById('card-footer-panel-rotation').addEventListener('input', () => this.updateActiveCard());
|
||||
|
||||
// Title style selection auto-update
|
||||
document.querySelectorAll('.title-style-option').forEach(option => {
|
||||
option.addEventListener('click', () => {
|
||||
@@ -374,6 +395,22 @@ class YotoCoversApp {
|
||||
document.getElementById('card-image-position').value = preset.imagePosition;
|
||||
}
|
||||
|
||||
// Set panel properties if defined in preset
|
||||
document.getElementById('card-panel-mode').checked = preset.panelMode === true;
|
||||
if (preset.titlePanel !== undefined) {
|
||||
document.getElementById('card-title-panel-border').value = preset.titlePanel.borderWidth;
|
||||
document.getElementById('card-title-panel-rotation').value = preset.titlePanel.rotation;
|
||||
document.getElementById('card-title-panel-expand').checked = preset.titlePanel.expand;
|
||||
}
|
||||
if (preset.imagePanel !== undefined) {
|
||||
document.getElementById('card-image-panel-border').value = preset.imagePanel.borderWidth;
|
||||
document.getElementById('card-image-panel-rotation').value = preset.imagePanel.rotation;
|
||||
}
|
||||
if (preset.footerPanel !== undefined) {
|
||||
document.getElementById('card-footer-panel-border').value = preset.footerPanel.borderWidth;
|
||||
document.getElementById('card-footer-panel-rotation').value = preset.footerPanel.rotation;
|
||||
}
|
||||
|
||||
this.updateActiveCard();
|
||||
}
|
||||
|
||||
@@ -395,7 +432,19 @@ class YotoCoversApp {
|
||||
showTitle: true,
|
||||
showFooter: true,
|
||||
imageFit: 'cover',
|
||||
imagePosition: 'center'
|
||||
imagePosition: 'center',
|
||||
titleFontSize: 20,
|
||||
footerFontSize: 12,
|
||||
titleX: 50,
|
||||
titleY: 20,
|
||||
footerX: 50,
|
||||
footerY: 90,
|
||||
titleDraggable: false,
|
||||
footerDraggable: false,
|
||||
panelMode: false,
|
||||
titlePanel: { borderWidth: 0, rotation: 0, expand: false },
|
||||
imagePanel: { borderWidth: 0, rotation: 0 },
|
||||
footerPanel: { borderWidth: 0, rotation: 0 }
|
||||
};
|
||||
this.cardVariations.push(variation);
|
||||
this.updateCardList();
|
||||
@@ -508,6 +557,20 @@ class YotoCoversApp {
|
||||
if (preset.fullBleed !== undefined && variation.fullBleed !== preset.fullBleed) matches = false;
|
||||
if (preset.showFooter !== undefined && variation.showFooter !== preset.showFooter) matches = false;
|
||||
if (preset.imagePosition !== undefined && variation.imagePosition !== preset.imagePosition) matches = false;
|
||||
if (preset.panelMode !== undefined && variation.panelMode !== preset.panelMode) matches = false;
|
||||
if (preset.titlePanel !== undefined) {
|
||||
if (variation.titlePanel?.borderWidth !== preset.titlePanel.borderWidth ||
|
||||
variation.titlePanel?.rotation !== preset.titlePanel.rotation ||
|
||||
variation.titlePanel?.expand !== preset.titlePanel.expand) matches = false;
|
||||
}
|
||||
if (preset.imagePanel !== undefined) {
|
||||
if (variation.imagePanel?.borderWidth !== preset.imagePanel.borderWidth ||
|
||||
variation.imagePanel?.rotation !== preset.imagePanel.rotation) matches = false;
|
||||
}
|
||||
if (preset.footerPanel !== undefined) {
|
||||
if (variation.footerPanel?.borderWidth !== preset.footerPanel.borderWidth ||
|
||||
variation.footerPanel?.rotation !== preset.footerPanel.rotation) matches = false;
|
||||
}
|
||||
|
||||
if (matches) {
|
||||
matchingPreset = presetName;
|
||||
@@ -552,13 +615,29 @@ class YotoCoversApp {
|
||||
// Set title options
|
||||
document.getElementById('card-title-font-size').value = variation.titleFontSize || 20;
|
||||
document.getElementById('card-title-x').value = variation.titleX !== undefined ? variation.titleX : 50;
|
||||
document.getElementById('card-title-y').value = variation.titleY !== undefined ? variation.titleY : 10;
|
||||
document.getElementById('card-title-y').value = variation.titleY !== undefined ? variation.titleY : 20;
|
||||
document.getElementById('card-title-draggable').checked = variation.titleDraggable === true;
|
||||
|
||||
// Set footer options
|
||||
document.getElementById('card-footer-font-size').value = variation.footerFontSize || 12;
|
||||
document.getElementById('card-footer-x').value = variation.footerX !== undefined ? variation.footerX : 50;
|
||||
document.getElementById('card-footer-y').value = variation.footerY !== undefined ? variation.footerY : 90;
|
||||
document.getElementById('card-footer-draggable').checked = variation.footerDraggable === true;
|
||||
|
||||
// Set image options
|
||||
document.getElementById('card-image-fit').value = variation.imageFit || 'cover';
|
||||
document.getElementById('card-image-position').value = variation.imagePosition || 'center';
|
||||
|
||||
// Set panel options
|
||||
document.getElementById('card-panel-mode').checked = variation.panelMode === true;
|
||||
document.getElementById('card-title-panel-border').value = variation.titlePanel?.borderWidth || 0;
|
||||
document.getElementById('card-title-panel-rotation').value = variation.titlePanel?.rotation || 0;
|
||||
document.getElementById('card-title-panel-expand').checked = variation.titlePanel?.expand === true;
|
||||
document.getElementById('card-image-panel-border').value = variation.imagePanel?.borderWidth || 0;
|
||||
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;
|
||||
|
||||
// Populate image options
|
||||
const imageSelect = document.getElementById('card-image-select');
|
||||
imageSelect.innerHTML = '<option value="-1">Use default image</option>';
|
||||
@@ -579,9 +658,11 @@ class YotoCoversApp {
|
||||
updateSectionVisibility() {
|
||||
const showTitle = document.getElementById('card-show-title').checked;
|
||||
const showFooter = document.getElementById('card-show-footer').checked;
|
||||
const panelMode = document.getElementById('card-panel-mode').checked;
|
||||
|
||||
const titleSection = document.getElementById('title-section');
|
||||
const footerSection = document.getElementById('footer-section');
|
||||
const panelSection = document.getElementById('panel-section');
|
||||
|
||||
if (showTitle) {
|
||||
titleSection.classList.remove('collapsed');
|
||||
@@ -594,6 +675,12 @@ class YotoCoversApp {
|
||||
} else {
|
||||
footerSection.classList.add('collapsed');
|
||||
}
|
||||
|
||||
if (panelMode) {
|
||||
panelSection.classList.remove('collapsed');
|
||||
} else {
|
||||
panelSection.classList.add('collapsed');
|
||||
}
|
||||
}
|
||||
|
||||
initDragFunctionality() {
|
||||
@@ -706,6 +793,89 @@ class YotoCoversApp {
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', handleMouseUp);
|
||||
});
|
||||
|
||||
// Initialize footer drag functionality
|
||||
const footers = iframeDoc.querySelectorAll('.footer, .comic-footer-panel');
|
||||
console.log('Found footers:', footers.length);
|
||||
|
||||
footers.forEach((footer, index) => {
|
||||
console.log(`Footer ${index} cursor:`, footer.style.cursor);
|
||||
// Only make draggable if cursor is set to 'move' and not a comic panel
|
||||
if (footer.style.cursor !== 'move' || footer.classList.contains('comic-footer-panel')) {
|
||||
console.log(`Footer ${index} not draggable, skipping`);
|
||||
return;
|
||||
}
|
||||
|
||||
let isDragging = false;
|
||||
let startX, startY, initialLeft, initialBottom;
|
||||
|
||||
footer.addEventListener('mousedown', (e) => {
|
||||
console.log('Footer mousedown fired');
|
||||
if (footer.style.cursor !== 'move') return;
|
||||
|
||||
isDragging = true;
|
||||
|
||||
// Get iframe position and convert mouse coordinates to iframe-relative
|
||||
const iframeRect = iframe.getBoundingClientRect();
|
||||
startX = e.clientX - iframeRect.left;
|
||||
startY = e.clientY - iframeRect.top;
|
||||
|
||||
// Store initial position
|
||||
initialLeft = parseFloat(footer.style.left) || 50;
|
||||
initialBottom = parseFloat(footer.style.bottom) || 10;
|
||||
|
||||
footer.style.zIndex = '1000';
|
||||
footer.style.userSelect = 'none';
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
const handleMouseMove = (e) => {
|
||||
if (!isDragging) return;
|
||||
|
||||
// Get iframe position in main document
|
||||
const iframeRect = iframe.getBoundingClientRect();
|
||||
|
||||
// Convert mouse coordinates to be relative to iframe viewport
|
||||
const mouseX = e.clientX - iframeRect.left;
|
||||
const mouseY = e.clientY - iframeRect.top;
|
||||
|
||||
const card = footer.closest('.card');
|
||||
const cardRect = card.getBoundingClientRect();
|
||||
|
||||
// Calculate new position as percentage
|
||||
const deltaX = mouseX - startX;
|
||||
const deltaY = mouseY - startY;
|
||||
|
||||
const newLeftPercent = Math.max(0, Math.min(100, initialLeft + (deltaX / cardRect.width) * 100));
|
||||
const newBottomPercent = Math.max(0, Math.min(100, initialBottom - (deltaY / cardRect.height) * 100));
|
||||
|
||||
footer.style.left = `${newLeftPercent}%`;
|
||||
footer.style.bottom = `${newBottomPercent}%`;
|
||||
|
||||
// Update the card data
|
||||
const cardIndex = Array.from(card.parentElement.children).indexOf(card);
|
||||
if (this.cardVariations[cardIndex]) {
|
||||
this.cardVariations[cardIndex].footerX = Math.round(newLeftPercent);
|
||||
this.cardVariations[cardIndex].footerY = Math.round(100 - newBottomPercent);
|
||||
this.updateUI();
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
if (isDragging) {
|
||||
isDragging = false;
|
||||
footer.style.zIndex = '';
|
||||
footer.style.userSelect = '';
|
||||
}
|
||||
};
|
||||
|
||||
// Add event listeners to both iframe document and main document
|
||||
// to handle mouse events that might occur outside the iframe
|
||||
iframeDoc.addEventListener('mousemove', handleMouseMove);
|
||||
iframeDoc.addEventListener('mouseup', handleMouseUp);
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', handleMouseUp);
|
||||
});
|
||||
} catch (e) {
|
||||
// Ignore cross-origin errors
|
||||
console.log('Drag functionality initialization failed:', e);
|
||||
@@ -747,10 +917,30 @@ class YotoCoversApp {
|
||||
variation.titleX = parseInt(document.getElementById('card-title-x').value);
|
||||
variation.titleY = parseInt(document.getElementById('card-title-y').value);
|
||||
variation.titleDraggable = document.getElementById('card-title-draggable').checked;
|
||||
variation.footerFontSize = parseInt(document.getElementById('card-footer-font-size').value);
|
||||
variation.footerX = parseInt(document.getElementById('card-footer-x').value);
|
||||
variation.footerY = parseInt(document.getElementById('card-footer-y').value);
|
||||
variation.footerDraggable = document.getElementById('card-footer-draggable').checked;
|
||||
variation.imageFit = document.getElementById('card-image-fit').value;
|
||||
variation.imagePosition = document.getElementById('card-image-position').value;
|
||||
variation.imageIndex = parseInt(document.getElementById('card-image-select').value);
|
||||
|
||||
// Panel settings
|
||||
variation.panelMode = document.getElementById('card-panel-mode').checked;
|
||||
variation.titlePanel = {
|
||||
borderWidth: parseInt(document.getElementById('card-title-panel-border').value),
|
||||
rotation: parseInt(document.getElementById('card-title-panel-rotation').value),
|
||||
expand: document.getElementById('card-title-panel-expand').checked
|
||||
};
|
||||
variation.imagePanel = {
|
||||
borderWidth: parseInt(document.getElementById('card-image-panel-border').value),
|
||||
rotation: parseInt(document.getElementById('card-image-panel-rotation').value)
|
||||
};
|
||||
variation.footerPanel = {
|
||||
borderWidth: parseInt(document.getElementById('card-footer-panel-border').value),
|
||||
rotation: parseInt(document.getElementById('card-footer-panel-rotation').value)
|
||||
};
|
||||
|
||||
this.updateCardList();
|
||||
this.generatePreview();
|
||||
}
|
||||
@@ -832,7 +1022,9 @@ class YotoCoversApp {
|
||||
let imageIndex, cardTitle, cardFooter, cardTitleStyle, cardAccentColor, cardTitleColor, cardFullBleed, cardTitleShadow;
|
||||
let cardBackgroundStyle, cardFontFamily, cardFontWeight, cardFooterColor, cardShowTitle, cardShowFooter;
|
||||
let cardImageFit, cardImagePosition, cardTitleFontSize, cardTitleX, cardTitleY, cardTitleDraggable;
|
||||
let cardFooterFontSize, cardFooterX, cardFooterY, cardFooterDraggable;
|
||||
let cardShowAccent;
|
||||
let cardPanelMode, cardTitlePanel, cardImagePanel, cardFooterPanel;
|
||||
|
||||
if (this.cardVariations.length > 0) {
|
||||
// Use variation settings
|
||||
@@ -857,8 +1049,16 @@ class YotoCoversApp {
|
||||
cardImagePosition = (variation && variation.imagePosition) || 'center';
|
||||
cardTitleFontSize = (variation && variation.titleFontSize) || 20;
|
||||
cardTitleX = variation && variation.titleX !== undefined ? variation.titleX : 50;
|
||||
cardTitleY = variation && variation.titleY !== undefined ? variation.titleY : 10;
|
||||
cardTitleY = variation && variation.titleY !== undefined ? variation.titleY : 20;
|
||||
cardTitleDraggable = variation && variation.titleDraggable !== undefined ? variation.titleDraggable : false;
|
||||
cardFooterFontSize = (variation && variation.footerFontSize) || 12;
|
||||
cardFooterX = variation && variation.footerX !== undefined ? variation.footerX : 50;
|
||||
cardFooterY = variation && variation.footerY !== undefined ? variation.footerY : 90;
|
||||
cardFooterDraggable = variation && variation.footerDraggable !== undefined ? variation.footerDraggable : false;
|
||||
cardPanelMode = variation && variation.panelMode !== undefined ? variation.panelMode : false;
|
||||
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 };
|
||||
} else {
|
||||
// No variations - auto-assign images
|
||||
imageIndex = i < this.images.length ? i : 0;
|
||||
@@ -881,8 +1081,16 @@ class YotoCoversApp {
|
||||
cardImagePosition = 'center';
|
||||
cardTitleFontSize = 20;
|
||||
cardTitleX = 50;
|
||||
cardTitleY = 10;
|
||||
cardTitleY = 20;
|
||||
cardTitleDraggable = false;
|
||||
cardFooterFontSize = 12;
|
||||
cardFooterX = 50;
|
||||
cardFooterY = 90;
|
||||
cardFooterDraggable = false;
|
||||
cardPanelMode = false;
|
||||
cardTitlePanel = { borderWidth: 0, rotation: 0, expand: false };
|
||||
cardImagePanel = { borderWidth: 0, rotation: 0 };
|
||||
cardFooterPanel = { borderWidth: 0, rotation: 0 };
|
||||
}
|
||||
|
||||
const imageUrl = this.images.length > 0 ? this.images[imageIndex].dataUrl : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';
|
||||
@@ -909,6 +1117,14 @@ class YotoCoversApp {
|
||||
titleX: cardTitleX,
|
||||
titleY: cardTitleY,
|
||||
titleDraggable: cardTitleDraggable,
|
||||
footerFontSize: cardFooterFontSize,
|
||||
footerX: cardFooterX,
|
||||
footerY: cardFooterY,
|
||||
footerDraggable: cardFooterDraggable,
|
||||
panelMode: cardPanelMode,
|
||||
titlePanel: cardTitlePanel,
|
||||
imagePanel: cardImagePanel,
|
||||
footerPanel: cardFooterPanel,
|
||||
isVisible: true
|
||||
});
|
||||
} else {
|
||||
@@ -935,6 +1151,10 @@ class YotoCoversApp {
|
||||
titleX: 50,
|
||||
titleY: 10,
|
||||
titleDraggable: false,
|
||||
panelMode: false,
|
||||
titlePanel: { borderWidth: 0, rotation: 0, expand: false },
|
||||
imagePanel: { borderWidth: 0, rotation: 0 },
|
||||
footerPanel: { borderWidth: 0, rotation: 0 },
|
||||
isVisible: false
|
||||
});
|
||||
}
|
||||
@@ -995,29 +1215,66 @@ class YotoCoversApp {
|
||||
overflow: hidden;
|
||||
">
|
||||
${card.isVisible ? `
|
||||
${card.backgroundStyle === 'comic' ? `
|
||||
${card.panelMode ? `
|
||||
<!-- Comic Style Panels -->
|
||||
<div class="comic-panels">
|
||||
${card.showTitle ? `<div class="comic-panel comic-title-panel" style="
|
||||
${card.showTitle ? (card.titleStyle === 'split' ? `
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&display=swap');
|
||||
.comic-panel.comic-title-panel.split {
|
||||
--s: 4px;
|
||||
--o: calc(0.5 * var(--s));
|
||||
--sl: calc(50% - 0.5px), #0000 calc(50% + 0.5px);
|
||||
position: absolute;
|
||||
left: 8%;
|
||||
top: 6%;
|
||||
width: 40%;
|
||||
height: 15%;
|
||||
border: 2px solid #000;
|
||||
${card.titlePanel.expand ? 'width: auto; min-width: 30%; max-width: 60%;' : 'width: 40%;'}
|
||||
height: ${card.titlePanel.expand ? 'auto' : '15%'};
|
||||
border: ${card.titlePanel.borderWidth}px solid #000;
|
||||
place-self: center;
|
||||
place-content: center;
|
||||
padding: calc(0.1em) 0.05em 0 0;
|
||||
background:
|
||||
linear-gradient(22.5deg, #fff var(--sl)) text,
|
||||
linear-gradient(22.5deg, #000 var(--sl)),
|
||||
linear-gradient(${card.titleColor} 50%, #0000 0)
|
||||
0 0/ 1% var(--s);
|
||||
color: #0000;
|
||||
-webkit-text-stroke: #fff calc(var(--o));
|
||||
text-transform: uppercase;
|
||||
mix-blend-mode: screen;
|
||||
font: clamp(1em, min(65vh, 35vw), 2em)/ 0.75 'Bebas Neue', sans-serif;
|
||||
transform: rotate(${card.titlePanel.rotation}deg);
|
||||
box-shadow: 2px 2px 4px rgba(0,0,0,0.2);
|
||||
@supports (line-height: 1cap) { line-height: 1cap; }
|
||||
}
|
||||
@media (min-width: 480px) and (min-height: 320px) {
|
||||
.comic-panel.comic-title-panel.split { --s: 6px; }
|
||||
}
|
||||
</style>
|
||||
<div class="comic-panel comic-title-panel split">${this.escapeHtml(card.title)}</div>
|
||||
` : `<div class="comic-panel comic-title-panel" style="
|
||||
position: absolute;
|
||||
left: 8%;
|
||||
top: 6%;
|
||||
${card.titlePanel.expand ? 'width: auto; min-width: 30%; max-width: 60%;' : 'width: 40%;'}
|
||||
height: ${card.titlePanel.expand ? 'auto' : '15%'};
|
||||
border: ${card.titlePanel.borderWidth}px solid #000;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
white-space: pre-line;
|
||||
text-align: center;
|
||||
font-size: ${card.titleFontSize * 0.8}px;
|
||||
font-weight: ${card.fontWeight === 'bold' ? '700' : '400'};
|
||||
font-family: '${card.fontFamily}', serif;
|
||||
color: ${card.titleColor};
|
||||
${card.titleShadow ? 'text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);' : ''}
|
||||
transform: rotate(-2deg);
|
||||
transform: rotate(${card.titlePanel.rotation}deg);
|
||||
box-shadow: 2px 2px 4px rgba(0,0,0,0.2);
|
||||
${card.titleDraggable && card.backgroundStyle !== 'comic' ? 'cursor: move; user-select: none;' : ''}
|
||||
" data-panel-type="title">${this.escapeHtml(card.title)}</div>` : ''}
|
||||
${card.titleDraggable && !card.panelMode ? 'cursor: move; user-select: none;' : ''}
|
||||
" data-panel-type="title">${this.escapeHtml(card.title)}</div>`) : ''}
|
||||
|
||||
<div class="comic-panel comic-image-panel" style="
|
||||
position: absolute;
|
||||
@@ -1025,9 +1282,9 @@ class YotoCoversApp {
|
||||
top: 25%;
|
||||
right: 15%;
|
||||
bottom: ${card.showFooter ? '35%' : '15%'};
|
||||
border: 3px solid #000;
|
||||
border: ${card.imagePanel.borderWidth}px solid #000;
|
||||
background: ${card.fullBleed ? `url('${card.imageUrl}') no-repeat ${card.imagePosition}/${card.imageFit}` : '#f8f9fa'};
|
||||
transform: rotate(1deg);
|
||||
transform: rotate(${card.imagePanel.rotation}deg);
|
||||
box-shadow: 3px 3px 6px rgba(0,0,0,0.3);
|
||||
overflow: hidden;
|
||||
">
|
||||
@@ -1054,7 +1311,7 @@ class YotoCoversApp {
|
||||
bottom: 6%;
|
||||
width: 35%;
|
||||
height: 12%;
|
||||
border: 2px solid #000;
|
||||
border: ${card.footerPanel.borderWidth}px solid #000;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -1063,15 +1320,49 @@ class YotoCoversApp {
|
||||
font-weight: ${card.fontWeight};
|
||||
font-family: '${card.fontFamily}', serif;
|
||||
color: ${card.footerColor};
|
||||
transform: rotate(3deg);
|
||||
transform: rotate(${card.footerPanel.rotation}deg);
|
||||
box-shadow: 2px 2px 4px rgba(0,0,0,0.2);
|
||||
text-align: center;
|
||||
padding: 2px;
|
||||
white-space: pre-line;
|
||||
">${this.escapeHtml(card.footer)}</div>` : ''}
|
||||
</div>
|
||||
` : `
|
||||
<!-- Standard Layout -->
|
||||
${card.showTitle ? `<div class="title ${card.titleStyle}" data-heading="${this.escapeHtml(card.title)}" style="
|
||||
${card.showTitle ? (card.titleStyle === 'split' ? `
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&display=swap');
|
||||
.title.split {
|
||||
--s: 4px;
|
||||
--o: calc(0.5 * var(--s));
|
||||
--sl: calc(50% - 0.5px), #0000 calc(50% + 0.5px);
|
||||
position: absolute;
|
||||
left: ${card.titleX}%;
|
||||
top: ${card.titleY}%;
|
||||
transform: translate(-50%, -50%);
|
||||
place-self: center;
|
||||
place-content: center;
|
||||
padding: calc(0.1em) 0.05em 0 0;
|
||||
height: calc(1lh - var(--o));
|
||||
background:
|
||||
linear-gradient(22.5deg, #fff var(--sl)) text,
|
||||
linear-gradient(22.5deg, #000 var(--sl)),
|
||||
linear-gradient(${card.titleColor} 50%, #0000 0)
|
||||
0 0/ 1% var(--s);
|
||||
color: #0000;
|
||||
-webkit-text-stroke: #fff calc(var(--o));
|
||||
text-transform: uppercase;
|
||||
mix-blend-mode: screen;
|
||||
font: clamp(2em, min(65vh, 35vw), 3.9em)/ 0.75 'Bebas Neue', sans-serif;
|
||||
${card.titleDraggable ? 'cursor: move; user-select: none;' : ''}
|
||||
@supports (line-height: 1cap) { line-height: 1cap; }
|
||||
}
|
||||
@media (min-width: 480px) and (min-height: 320px) {
|
||||
.title.split { --s: 6px; }
|
||||
}
|
||||
</style>
|
||||
<div class="title split">${this.escapeHtml(card.title)}</div>
|
||||
` : `<div class="title ${card.titleStyle}" data-heading="${this.escapeHtml(card.title)}" style="
|
||||
color: ${card.titleColor};
|
||||
font-weight: ${card.fontWeight === 'bold' ? '700' : '400'};
|
||||
font-family: '${card.fontFamily}', serif;
|
||||
@@ -1081,14 +1372,24 @@ class YotoCoversApp {
|
||||
left: ${card.titleX}%;
|
||||
top: ${card.titleY}%;
|
||||
transform: translate(-50%, -50%);
|
||||
white-space: pre-line;
|
||||
text-align: center;
|
||||
${card.titleDraggable ? 'cursor: move; user-select: none;' : ''}
|
||||
">${this.escapeHtml(card.title)}</div>` : ''}
|
||||
">${this.escapeHtml(card.title)}</div>`) : ''}
|
||||
${!card.fullBleed ? `<div class="hero"><img src="${card.imageUrl}" alt="cover" style="object-fit: ${card.imageFit}; object-position: ${card.imagePosition};"/></div>` : ''}
|
||||
${card.showAccent ? `<div class="overlay" style="background: ${card.accentColor}40;"></div>` : `<div class="overlay" style="background: rgba(0,0,0,0);"></div>`}
|
||||
${card.showFooter ? `<div class="footer" style="
|
||||
color: ${card.footerColor};
|
||||
font-family: '${card.fontFamily}', serif;
|
||||
font-weight: ${card.fontWeight};
|
||||
font-size: ${card.footerFontSize}px;
|
||||
position: absolute;
|
||||
left: ${card.footerX}%;
|
||||
bottom: ${100 - card.footerY}%;
|
||||
transform: translate(-50%, 50%);
|
||||
white-space: pre-line;
|
||||
text-align: center;
|
||||
${card.footerDraggable ? 'cursor: move; user-select: none;' : ''}
|
||||
">${this.escapeHtml(card.footer)}</div>` : ''}
|
||||
`}
|
||||
` : ''}
|
||||
@@ -1269,7 +1570,7 @@ class YotoCoversApp {
|
||||
escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
return div.innerHTML.replace(/\n/g, '<br>');
|
||||
}
|
||||
|
||||
openPreviewInNewTab() {
|
||||
|
||||
+91
-2
@@ -496,6 +496,13 @@
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.panel-controls {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.form-group.inline {
|
||||
flex: 1;
|
||||
margin-bottom: 0;
|
||||
@@ -716,6 +723,64 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Panel Section -->
|
||||
<div class="config-section" id="panel-section">
|
||||
<h4>Panel Settings</h4>
|
||||
<div class="section-controls">
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="card-panel-mode">
|
||||
<label for="card-panel-mode">Enable panel mode</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section-content">
|
||||
<div class="form-group">
|
||||
<label>Title Panel</label>
|
||||
<div class="panel-controls">
|
||||
<div class="form-group inline">
|
||||
<label for="card-title-panel-border">Border Width</label>
|
||||
<input type="number" id="card-title-panel-border" value="0" min="0" max="10" step="1">
|
||||
</div>
|
||||
<div class="form-group inline">
|
||||
<label for="card-title-panel-rotation">Rotation (°)</label>
|
||||
<input type="number" id="card-title-panel-rotation" value="0" min="-10" max="10" step="1">
|
||||
</div>
|
||||
<div class="checkbox-group inline">
|
||||
<input type="checkbox" id="card-title-panel-expand">
|
||||
<label for="card-title-panel-expand">Expandable</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Image Panel</label>
|
||||
<div class="panel-controls">
|
||||
<div class="form-group inline">
|
||||
<label for="card-image-panel-border">Border Width</label>
|
||||
<input type="number" id="card-image-panel-border" value="0" min="0" max="10" step="1">
|
||||
</div>
|
||||
<div class="form-group inline">
|
||||
<label for="card-image-panel-rotation">Rotation (°)</label>
|
||||
<input type="number" id="card-image-panel-rotation" value="0" min="-10" max="10" step="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Footer Panel</label>
|
||||
<div class="panel-controls">
|
||||
<div class="form-group inline">
|
||||
<label for="card-footer-panel-border">Border Width</label>
|
||||
<input type="number" id="card-footer-panel-border" value="0" min="0" max="10" step="1">
|
||||
</div>
|
||||
<div class="form-group inline">
|
||||
<label for="card-footer-panel-rotation">Rotation (°)</label>
|
||||
<input type="number" id="card-footer-panel-rotation" value="0" min="-10" max="10" step="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Title Section -->
|
||||
<div class="config-section" id="title-section">
|
||||
<h4>Title</h4>
|
||||
@@ -728,7 +793,7 @@
|
||||
<div class="section-content">
|
||||
<div class="form-group">
|
||||
<label for="card-title-input">Title Text</label>
|
||||
<input type="text" id="card-title-input" placeholder="Enter card title">
|
||||
<textarea id="card-title-input" placeholder="Enter card title" rows="2"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@@ -738,6 +803,7 @@
|
||||
<div class="title-style-option" data-style="condensed">Condensed</div>
|
||||
<div class="title-style-option" data-style="outlined">Outlined</div>
|
||||
<div class="title-style-option selected" data-style="classic">Classic</div>
|
||||
<div class="title-style-option" data-style="split">Split</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -848,7 +914,7 @@
|
||||
<div class="section-content">
|
||||
<div class="form-group">
|
||||
<label for="card-footer-input">Footer Text</label>
|
||||
<input type="text" id="card-footer-input" placeholder="Enter footer text">
|
||||
<textarea id="card-footer-input" placeholder="Enter footer text" rows="2"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@@ -858,6 +924,29 @@
|
||||
<input type="text" id="card-footer-color-text" value="#111111" class="color-value" readonly>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="card-footer-font-size">Footer Font Size (px)</label>
|
||||
<input type="number" id="card-footer-font-size" value="12" min="8" max="100" step="2">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Footer Position</label>
|
||||
<div class="position-controls">
|
||||
<div class="form-group inline">
|
||||
<label for="card-footer-x">X (%)</label>
|
||||
<input type="number" id="card-footer-x" value="50" min="0" max="100" step="1">
|
||||
</div>
|
||||
<div class="form-group inline">
|
||||
<label for="card-footer-y">Y (%)</label>
|
||||
<input type="number" id="card-footer-y" value="90" min="0" max="100" step="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="card-footer-draggable">
|
||||
<label for="card-footer-draggable">Enable drag to reposition</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user