diff --git a/beango/css/beango.css b/beango/css/beango.css index d87d99a..5b6c2ba 100644 --- a/beango/css/beango.css +++ b/beango/css/beango.css @@ -32,18 +32,54 @@ body { border-style: solid; /* Keep border style */ /* border-color will be set by JS */ /* background-color or background-image will be set by JS */ - text-align: center; - padding: 8px; - word-wrap: break-word; - overflow-wrap: break-word; - hyphens: auto; - /* Add transition for background color changes */ + text-align: center; /* Keep for potential fallback/spacing */ + padding: 8px; /* Keep padding on the main cell */ + /* REMOVED text styling from here */ + /* word-wrap: break-word; */ + /* overflow-wrap: break-word; */ + /* hyphens: auto; */ + /* font-size: 1.5rem; */ transition: background-color 0.2s ease-in-out, transform 0.1s ease-in-out, box-shadow 0.1s ease-in-out, filter 0.15s ease-in-out, border-color 0.2s ease-in-out, opacity 0.2s ease-in-out; display: flex; justify-content: center; align-items: center; border-radius: 8px; - font-size: 1.5rem; + position: relative; /* Ensure stacking context for z-index */ + overflow: hidden; /* Hide potential image overflow */ +} + +/* NEW: Pseudo-element for background image layer */ +.bingo-cell::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: var(--bg-image-url, none); + background-size: cover; + background-position: center center; + background-repeat: no-repeat; + z-index: 1; /* Place ABOVE the text content */ + opacity: var(--bg-image-opacity, 1); /* NEW: Use variable for opacity */ + /* Inherit border radius if needed */ + /* border-radius: inherit; */ +} + +/* NEW: Styles for the text span inside the cell */ +.bingo-cell-text { + display: inline-block; /* Or block, depending on desired wrapping */ + word-wrap: break-word; + overflow-wrap: break-word; + hyphens: auto; + font-size: 1.5rem; /* Moved font size here */ + color: #000; /* Default text color (can be customized later if needed) */ + position: relative; /* Needed for z-index */ + z-index: 0; /* Place BELOW the pseudo-element */ + /* Remove background to allow parent background to show */ + background: transparent; + /* Max width to help with wrapping within padding */ + max-width: 100%; } /* Add darken filter on hover */ diff --git a/beango/index.html b/beango/index.html index 607e599..81ee1c3 100644 --- a/beango/index.html +++ b/beango/index.html @@ -187,6 +187,10 @@

If set, this image is layered over the background color.

+
+ + +
@@ -227,6 +231,10 @@
+
+ + +
diff --git a/beango/js/beango.js b/beango/js/beango.js index e3f588e..ed0e206 100644 --- a/beango/js/beango.js +++ b/beango/js/beango.js @@ -24,11 +24,13 @@ const LS_HEADER_TEXT_COLOR_OPACITY = 'beango_headerTextColorOpacity'; // New const LS_MARKED_COLOR = 'beango_markedColor'; const LS_MARKED_COLOR_OPACITY = 'beango_markedColorOpacity'; // New (replaces LS_MARKED_OPACITY) const LS_MARKED_IMAGE_URL = 'beango_markedImageUrl'; +const LS_MARKED_IMAGE_OPACITY = 'beango_markedImageOpacity'; // New const LS_CELL_BORDER_COLOR = 'beango_cellBorderColor'; const LS_CELL_BORDER_OPACITY = 'beango_cellBorderOpacity'; // New const LS_CELL_BG_COLOR = 'beango_cellBgColor'; const LS_CELL_BG_OPACITY = 'beango_cellBgOpacity'; // New const LS_CELL_BG_IMAGE_URL = 'beango_cellBgImageUrl'; +const LS_CELL_BG_IMAGE_OPACITY = 'beango_cellBgImageOpacity'; // New const LS_MARKED_BORDER_COLOR = 'beango_markedBorderColor'; const LS_MARKED_BORDER_OPACITY = 'beango_markedBorderOpacity'; // New const LS_BOARD_BG_COLOR = 'beango_boardBgColor'; @@ -50,11 +52,13 @@ const DEFAULT_HEADER_TEXT_COLOR_OPACITY = 100; // New const DEFAULT_MARKED_COLOR = '#fde047'; // Default yellow const DEFAULT_MARKED_COLOR_OPACITY = 80; // New (replaces DEFAULT_MARKED_OPACITY) const DEFAULT_MARKED_IMAGE_URL = ''; +const DEFAULT_MARKED_IMAGE_OPACITY = 100; // New const DEFAULT_CELL_BORDER_COLOR = '#8B4513'; // Default brown const DEFAULT_CELL_BORDER_OPACITY = 100; // New const DEFAULT_CELL_BG_COLOR = '#F5F5DC'; // Default beige const DEFAULT_CELL_BG_OPACITY = 100; // New const DEFAULT_CELL_BG_IMAGE_URL = ''; // Default no image +const DEFAULT_CELL_BG_IMAGE_OPACITY = 100; // New const DEFAULT_MARKED_BORDER_COLOR = '#ca8a04'; // Default darker yellow/orange (Tailwind yellow-600) const DEFAULT_MARKED_BORDER_OPACITY = 100; // New const DEFAULT_BOARD_BG_COLOR = '#ffffff'; // Default white @@ -258,6 +262,7 @@ function saveMarkedStyleSettings() { localStorage.setItem(LS_MARKED_COLOR, document.getElementById('marked-color-picker').value); localStorage.setItem(LS_MARKED_COLOR_OPACITY, document.getElementById('marked-color-opacity-slider').value); localStorage.setItem(LS_MARKED_IMAGE_URL, document.getElementById('marked-image-url-input').value); + localStorage.setItem(LS_MARKED_IMAGE_OPACITY, document.getElementById('marked-image-opacity-slider').value); // Save image opacity // Save marked border color and its opacity localStorage.setItem(LS_MARKED_BORDER_COLOR, document.getElementById('marked-border-color-picker').value); @@ -272,6 +277,7 @@ function applyMarkedCellStyle(cell) { const color = localStorage.getItem(LS_MARKED_COLOR) || DEFAULT_MARKED_COLOR; const colorOpacity = parseInt(localStorage.getItem(LS_MARKED_COLOR_OPACITY) || DEFAULT_MARKED_COLOR_OPACITY, 10); const imageUrl = localStorage.getItem(LS_MARKED_IMAGE_URL) || DEFAULT_MARKED_IMAGE_URL; + const imageOpacity = parseInt(localStorage.getItem(LS_MARKED_IMAGE_OPACITY) || DEFAULT_MARKED_IMAGE_OPACITY, 10); const borderColor = localStorage.getItem(LS_MARKED_BORDER_COLOR) || DEFAULT_MARKED_BORDER_COLOR; const borderOpacity = parseInt(localStorage.getItem(LS_MARKED_BORDER_OPACITY) || DEFAULT_MARKED_BORDER_OPACITY, 10); @@ -287,15 +293,16 @@ function applyMarkedCellStyle(cell) { // Always apply the background color + opacity cell.style.backgroundColor = hexToRgba(color, colorOpacity); - // Apply background image if URL exists + // Apply background image if URL exists via custom property if (imageUrl && imageUrl.trim() !== '') { - cell.style.backgroundImage = `url('${imageUrl}')`; - cell.style.backgroundSize = 'cover'; // Or adjust as needed + cell.style.setProperty('--bg-image-url', `url('${imageUrl}')`); + cell.style.setProperty('--bg-image-opacity', imageOpacity / 100); + cell.style.backgroundSize = 'cover'; cell.style.backgroundPosition = 'center center'; cell.style.backgroundRepeat = 'no-repeat'; } else { - // Explicitly clear background image if no URL - cell.style.backgroundImage = 'none'; + // Explicitly clear background image custom property + cell.style.removeProperty('--bg-image-url'); } } else { @@ -398,6 +405,7 @@ function saveCellStyleSettings() { localStorage.setItem(LS_CELL_BG_COLOR, document.getElementById('cell-background-color-picker').value); localStorage.setItem(LS_CELL_BG_OPACITY, document.getElementById('cell-background-opacity-slider').value); localStorage.setItem(LS_CELL_BG_IMAGE_URL, document.getElementById('cell-background-image-url-input').value); + localStorage.setItem(LS_CELL_BG_IMAGE_OPACITY, document.getElementById('cell-background-image-opacity-slider').value); // Save image opacity } // --- Apply saved default cell styles to a cell --- @@ -409,19 +417,23 @@ function applyCellStyle(cell) { const bgColor = localStorage.getItem(LS_CELL_BG_COLOR) || DEFAULT_CELL_BG_COLOR; const bgOpacity = parseInt(localStorage.getItem(LS_CELL_BG_OPACITY) || DEFAULT_CELL_BG_OPACITY, 10); const bgImageUrl = localStorage.getItem(LS_CELL_BG_IMAGE_URL) || DEFAULT_CELL_BG_IMAGE_URL; + const bgImageOpacity = parseInt(localStorage.getItem(LS_CELL_BG_IMAGE_OPACITY) || DEFAULT_CELL_BG_IMAGE_OPACITY, 10); cell.style.borderColor = hexToRgba(borderColor, borderOpacity); cell.style.opacity = ''; // Reset direct opacity, handled by color if (bgImageUrl) { - cell.style.backgroundImage = `url('${bgImageUrl}')`; + cell.style.setProperty('--bg-image-url', `url('${bgImageUrl}')`); // NEW: Set CSS custom property + cell.style.setProperty('--bg-image-opacity', bgImageOpacity / 100); // Set image opacity property cell.style.backgroundSize = 'cover'; cell.style.backgroundPosition = 'center center'; cell.style.backgroundRepeat = 'no-repeat'; // Set fallback color with its specific opacity cell.style.backgroundColor = hexToRgba(bgColor, bgOpacity); } else { - cell.style.backgroundImage = ''; // Clear image if URL is removed + // Clear image properties if no URL + cell.style.removeProperty('--bg-image-url'); // NEW: Remove CSS custom property + cell.style.removeProperty('--bg-image-opacity'); // Remove image opacity property cell.style.backgroundColor = hexToRgba(bgColor, bgOpacity); } } @@ -530,17 +542,24 @@ function generateBoard() { // Shuffle the items specifically for display (use currentItems which has the right size) displayedItems = shuffleArray([...currentItems]); - const board = document.getElementById('bingo-board'); - board.innerHTML = ''; // Clear previous board + const board = document.getElementById("bingo-board"); + board.innerHTML = ""; // Clear previous board board.style.gridTemplateColumns = `repeat(${size}, minmax(0, 1fr))`; displayedItems.forEach((item, index) => { - const cell = document.createElement('div'); - cell.classList.add('bingo-cell', 'cursor-pointer'); - cell.textContent = item; + const cell = document.createElement("div"); + cell.classList.add("bingo-cell", "cursor-pointer"); + // cell.textContent = item; // OLD WAY cell.dataset.index = index; // Add index for saving marks cell.onclick = () => selectCell(cell); applyCellStyle(cell); // Apply default styles upon creation + + // NEW: Create span for text content + const textSpan = document.createElement("span"); + textSpan.classList.add("bingo-cell-text"); + textSpan.textContent = item; + cell.appendChild(textSpan); + board.appendChild(cell); }); @@ -646,8 +665,8 @@ function randomizeBoard() { displayedItems = shuffleArray([...itemsToShuffle]); // Shuffle the items for display - const board = document.getElementById('bingo-board'); - const cells = board.querySelectorAll('.bingo-cell'); + const board = document.getElementById("bingo-board"); + const cells = board.querySelectorAll(".bingo-cell"); if (cells.length !== displayedItems.length) { showNotification("Cannot randomize: Board size mismatch. Please generate the board again.", 'error'); @@ -655,10 +674,19 @@ function randomizeBoard() { } cells.forEach((cell, index) => { - cell.textContent = displayedItems[index]; - cell.classList.remove('marked'); // Clear marks visually + // cell.textContent = displayedItems[index]; // OLD WAY + // NEW: Update or create text span + let textSpan = cell.querySelector(".bingo-cell-text"); + if (!textSpan) { // Should exist, but create if missing + textSpan = document.createElement("span"); + textSpan.classList.add("bingo-cell-text"); + cell.innerHTML = + cell.appendChild(textSpan); + } + textSpan.textContent = displayedItems[index]; + + cell.classList.remove("marked"); // Clear marks visually applyMarkedCellStyle(cell); // Reset marked styles (which also calls applyCellStyle) - // applyCellStyle(cell); // Ensure default styles are applied (covered by applyMarkedCellStyle reset) }); clearMarks(false); // Clear visual marks @@ -807,11 +835,15 @@ function loadFromLocalStorage() { const savedMarkedColor = localStorage.getItem(LS_MARKED_COLOR) || DEFAULT_MARKED_COLOR; const savedMarkedColorOpacity = localStorage.getItem(LS_MARKED_COLOR_OPACITY) || DEFAULT_MARKED_COLOR_OPACITY; const savedMarkedImageUrl = localStorage.getItem(LS_MARKED_IMAGE_URL) || DEFAULT_MARKED_IMAGE_URL; + const savedMarkedImageOpacity = localStorage.getItem(LS_MARKED_IMAGE_OPACITY) || DEFAULT_MARKED_IMAGE_OPACITY; + console.log(`LOAD: LS_MARKED_IMAGE_OPACITY = ${localStorage.getItem(LS_MARKED_IMAGE_OPACITY)}, using: ${savedMarkedImageOpacity}`); // DEBUG LOG const savedCellBorderColor = localStorage.getItem(LS_CELL_BORDER_COLOR) || DEFAULT_CELL_BORDER_COLOR; const savedCellBorderOpacity = localStorage.getItem(LS_CELL_BORDER_OPACITY) || DEFAULT_CELL_BORDER_OPACITY; const savedCellBgColor = localStorage.getItem(LS_CELL_BG_COLOR) || DEFAULT_CELL_BG_COLOR; const savedCellBgOpacity = localStorage.getItem(LS_CELL_BG_OPACITY) || DEFAULT_CELL_BG_OPACITY; const savedCellBgImageUrl = localStorage.getItem(LS_CELL_BG_IMAGE_URL) || DEFAULT_CELL_BG_IMAGE_URL; + const savedCellBgImageOpacity = localStorage.getItem(LS_CELL_BG_IMAGE_OPACITY) || DEFAULT_CELL_BG_IMAGE_OPACITY; + console.log(`LOAD: LS_CELL_BG_IMAGE_OPACITY = ${localStorage.getItem(LS_CELL_BG_IMAGE_OPACITY)}, using: ${savedCellBgImageOpacity}`); // DEBUG LOG const savedMarkedBorderColor = localStorage.getItem(LS_MARKED_BORDER_COLOR) || DEFAULT_MARKED_BORDER_COLOR; const savedMarkedBorderOpacity = localStorage.getItem(LS_MARKED_BORDER_OPACITY) || DEFAULT_MARKED_BORDER_OPACITY; const savedBoardBgColor = localStorage.getItem(LS_BOARD_BG_COLOR) || DEFAULT_BOARD_BG_COLOR; @@ -885,12 +917,19 @@ function loadFromLocalStorage() { displayedItems.forEach((item, index) => { const cell = document.createElement('div'); cell.classList.add('bingo-cell', 'cursor-pointer'); - cell.textContent = item; + // cell.textContent = item; // OLD WAY cell.dataset.index = index; // Ensure index is set for loading marks correctly cell.onclick = () => selectCell(cell); applyCellStyle(cell); // Apply default cell style first + + // NEW: Create span for text content + const textSpan = document.createElement("span"); + textSpan.classList.add("bingo-cell-text"); + textSpan.textContent = item; + cell.appendChild(textSpan); + if (markedIndices.includes(index)) { - cell.classList.add('marked'); // Apply saved mark using 'marked' class + cell.classList.add('marked'); // Apply saved mark using \'marked\' class // Apply dynamic marked styles AFTER adding the class and default styles applyMarkedCellStyle(cell); } @@ -947,20 +986,39 @@ function loadFromLocalStorage() { document.getElementById('marked-color-opacity-slider').value = savedMarkedColorOpacity; if(document.getElementById('marked-color-opacity-value')) document.getElementById('marked-color-opacity-value').textContent = savedMarkedColorOpacity; document.getElementById('marked-image-url-input').value = savedMarkedImageUrl; + const markedImageOpacitySlider = document.getElementById('marked-image-opacity-slider'); + const markedImageOpacityValueSpan = document.getElementById('marked-image-opacity-value'); + if (markedImageOpacitySlider) markedImageOpacitySlider.value = savedMarkedImageOpacity; + if (markedImageOpacityValueSpan) markedImageOpacityValueSpan.textContent = savedMarkedImageOpacity; + document.getElementById('marked-border-color-picker').value = savedMarkedBorderColor; - document.getElementById('marked-border-opacity-slider').value = savedMarkedBorderOpacity; - if(document.getElementById('marked-border-opacity-value')) document.getElementById('marked-border-opacity-value').textContent = savedMarkedBorderOpacity; + const markedBorderOpacitySlider = document.getElementById('marked-border-opacity-slider'); + const markedBorderOpacityValueSpan = document.getElementById('marked-border-opacity-value'); + if (markedBorderOpacitySlider) markedBorderOpacitySlider.value = savedMarkedBorderOpacity; + if (markedBorderOpacityValueSpan) markedBorderOpacityValueSpan.textContent = savedMarkedBorderOpacity; + refreshMarkedCellStyles(); // Apply the loaded marked styles // --- End Restore Marked Style Settings --- // --- Restore Default Cell Style Settings --- document.getElementById('cell-border-color-picker').value = savedCellBorderColor; - document.getElementById('cell-border-opacity-slider').value = savedCellBorderOpacity; - if(document.getElementById('cell-border-opacity-value')) document.getElementById('cell-border-opacity-value').textContent = savedCellBorderOpacity; + const cellBorderOpacitySlider = document.getElementById('cell-border-opacity-slider'); + const cellBorderOpacityValueSpan = document.getElementById('cell-border-opacity-value'); + if (cellBorderOpacitySlider) cellBorderOpacitySlider.value = savedCellBorderOpacity; + if (cellBorderOpacityValueSpan) cellBorderOpacityValueSpan.textContent = savedCellBorderOpacity; + document.getElementById('cell-background-color-picker').value = savedCellBgColor; - document.getElementById('cell-background-opacity-slider').value = savedCellBgOpacity; - if(document.getElementById('cell-background-opacity-value')) document.getElementById('cell-background-opacity-value').textContent = savedCellBgOpacity; + const cellBackgroundOpacitySlider = document.getElementById('cell-background-opacity-slider'); + const cellBackgroundOpacityValueSpan = document.getElementById('cell-background-opacity-value'); + if (cellBackgroundOpacitySlider) cellBackgroundOpacitySlider.value = savedCellBgOpacity; + if (cellBackgroundOpacityValueSpan) cellBackgroundOpacityValueSpan.textContent = savedCellBgOpacity; + document.getElementById('cell-background-image-url-input').value = savedCellBgImageUrl; + const cellBackgroundImageOpacitySlider = document.getElementById('cell-background-image-opacity-slider'); + const cellBackgroundImageOpacityValueSpan = document.getElementById('cell-background-image-opacity-value'); + if (cellBackgroundImageOpacitySlider) cellBackgroundImageOpacitySlider.value = savedCellBgImageOpacity; + if (cellBackgroundImageOpacityValueSpan) cellBackgroundImageOpacityValueSpan.textContent = savedCellBgImageOpacity; + // Styles are applied during board creation loop above // --- End Restore Default Cell Style Settings --- @@ -968,7 +1026,10 @@ function loadFromLocalStorage() { document.getElementById('board-bg-color-picker').value = savedBoardBgColor; document.getElementById('board-bg-image-url-input').value = savedBoardBgImageUrl; document.getElementById('board-bg-color-opacity-slider').value = savedBoardBgColorOpacity; - if(document.getElementById('board-bg-color-opacity-value')) document.getElementById('board-bg-color-opacity-value').textContent = savedBoardBgColorOpacity; + const boardBgOpacitySlider = document.getElementById('board-bg-color-opacity-slider'); + const boardBgOpacityValueSpan = document.getElementById('board-bg-color-opacity-value'); + if (boardBgOpacitySlider) boardBgOpacitySlider.value = savedBoardBgColorOpacity; + if (boardBgOpacityValueSpan) boardBgOpacityValueSpan.textContent = savedBoardBgColorOpacity; applyBoardBgStyle(); // Apply loaded style // --- End Restore Board Background Settings --- @@ -1282,6 +1343,11 @@ document.addEventListener('DOMContentLoaded', () => { saveMarkedStyleSettings(); refreshMarkedCellStyles(); }); + document.getElementById('marked-image-opacity-slider').addEventListener('input', (e) => { + if(document.getElementById('marked-image-opacity-value')) document.getElementById('marked-image-opacity-value').textContent = e.target.value; + saveMarkedStyleSettings(); + refreshMarkedCellStyles(); + }); document.getElementById('marked-border-color-picker').addEventListener('input', () => { saveMarkedStyleSettings(); refreshMarkedCellStyles(); @@ -1315,6 +1381,11 @@ document.addEventListener('DOMContentLoaded', () => { saveCellStyleSettings(); refreshCellStyles(); }); + document.getElementById('cell-background-image-opacity-slider').addEventListener('input', (e) => { + if(document.getElementById('cell-background-image-opacity-value')) document.getElementById('cell-background-image-opacity-value').textContent = e.target.value; + saveCellStyleSettings(); + refreshCellStyles(); + }); // --- Add Event Listeners for Board Background Controls --- document.getElementById('board-bg-color-picker').addEventListener('input', () => {