Feature/display modes web UI (#61)

* Fix leaderboard gap to use display width instead of hardcoded values

- Replace hardcoded spacing (40px) with display_manager.matrix.width
- Update gap calculation to use display width for blank screen simulation
- Fix display width logging to show correct value
- Ensures gap between league rotations matches actual display width

* Add display width gap to news manager

- Add display width gap at the beginning of news content
- Update total_scroll_width calculation to include display width gap
- Modify create_scrolling_image to draw text after display width gap
- Ensures news starts with blank screen period matching display width
- Removed duplicate create_scrolling_image method

* add Live, Recent, Upcoming toggles to display modes on website
This commit is contained in:
Chuck
2025-09-24 13:34:19 -04:00
committed by GitHub
parent 7c18b5126e
commit b1295047e2
3 changed files with 147 additions and 31 deletions

View File

@@ -90,6 +90,56 @@
color: var(--warning-color);
}
/* Display Mode Toggle Styles */
.display-mode-toggle {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border: 2px solid #dee2e6;
border-radius: 8px;
padding: 12px;
margin: 8px 0;
transition: all 0.3s ease;
}
.display-mode-toggle:hover {
border-color: #6c757d;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.display-mode-toggle label {
display: flex;
align-items: center;
gap: 10px;
margin: 0;
cursor: pointer;
font-weight: 500;
transition: color 0.2s ease;
}
.display-mode-toggle label:hover {
color: var(--primary-color);
}
.display-mode-toggle input[type="checkbox"] {
width: 18px;
height: 18px;
accent-color: var(--secondary-color);
}
.display-mode-toggle .mode-icon {
font-size: 16px;
width: 20px;
text-align: center;
}
.display-mode-toggle .mode-label {
font-size: 14px;
font-weight: 600;
}
.mode-live { color: #e74c3c; }
.mode-recent { color: #f39c12; }
.mode-upcoming { color: #3498db; }
.main-grid {
display: grid;
grid-template-columns: 1fr;
@@ -3508,6 +3558,10 @@
const liveUpd = sec.live_update_interval ?? 30;
const recentUpd = sec.recent_update_interval ?? 3600;
const upcomingUpd = sec.upcoming_update_interval ?? 3600;
const displayModes = sec.display_modes || {};
const liveModeEnabled = displayModes[`${p}_live`] ?? true;
const recentModeEnabled = displayModes[`${p}_recent`] ?? true;
const upcomingModeEnabled = displayModes[`${p}_upcoming`] ?? true;
return `
<div style="border:1px solid #ddd; border-radius:6px; padding:12px; margin:10px 0;">
<div style="display:flex; justify-content: space-between; align-items:center; margin-bottom:8px;">
@@ -3522,6 +3576,32 @@
<button type="button" class="btn btn-secondary" onclick="stopOnDemand()"><i class="fas fa-ban"></i> Stop</button>
</div>
</div>
<div style="margin-top:15px;">
<h4 style="margin: 0 0 10px 0; color: var(--primary-color); font-size: 16px;">
<i class="fas fa-toggle-on"></i> Display Modes
</h4>
<div class="display-mode-toggle">
<label>
<input type="checkbox" data-league="${l.key}" class="sp-display-mode" data-mode="live" ${liveModeEnabled ? 'checked' : ''}>
<i class="fas fa-circle mode-icon mode-live"></i>
<span class="mode-label mode-live">Live Mode</span>
</label>
</div>
<div class="display-mode-toggle">
<label>
<input type="checkbox" data-league="${l.key}" class="sp-display-mode" data-mode="recent" ${recentModeEnabled ? 'checked' : ''}>
<i class="fas fa-history mode-icon mode-recent"></i>
<span class="mode-label mode-recent">Recent Mode</span>
</label>
</div>
<div class="display-mode-toggle">
<label>
<input type="checkbox" data-league="${l.key}" class="sp-display-mode" data-mode="upcoming" ${upcomingModeEnabled ? 'checked' : ''}>
<i class="fas fa-clock mode-icon mode-upcoming"></i>
<span class="mode-label mode-upcoming">Upcoming Mode</span>
</label>
</div>
</div>
<div class="form-row" style="margin-top:10px;">
<div class="form-group">
<label>Live Priority</label>
@@ -3569,6 +3649,36 @@
`;
}).join('');
container.innerHTML = html || 'No sports configuration found.';
// Add event listeners for display mode toggles
const displayModeCheckboxes = container.querySelectorAll('.sp-display-mode');
displayModeCheckboxes.forEach(checkbox => {
checkbox.addEventListener('change', function() {
const league = this.getAttribute('data-league');
const mode = this.getAttribute('data-mode');
const isEnabled = this.checked;
// Visual feedback
const label = this.closest('label');
const toggle = this.closest('.display-mode-toggle');
if (isEnabled) {
toggle.style.backgroundColor = 'rgba(46, 204, 113, 0.1)';
toggle.style.borderColor = '#2ecc71';
} else {
toggle.style.backgroundColor = 'rgba(231, 76, 60, 0.1)';
toggle.style.borderColor = '#e74c3c';
}
// Reset after a short delay
setTimeout(() => {
toggle.style.backgroundColor = '';
toggle.style.borderColor = '';
}, 1000);
showNotification(`${league.toUpperCase()} ${mode} mode ${isEnabled ? 'enabled' : 'disabled'}`, 'success');
});
});
} catch (err) {
document.getElementById('sports-config').textContent = 'Failed to load sports configuration';
}
@@ -3591,6 +3701,24 @@
const upcomingUpd = parseInt(document.querySelector(`.sp-upcoming-update[data-league="${key}"]`)?.value || '3600');
const recentCount = parseInt(document.querySelector(`.sp-recent-count[data-league="${key}"]`)?.value || '1');
const upcomingCount = parseInt(document.querySelector(`.sp-upcoming-count[data-league="${key}"]`)?.value || '1');
// Get display modes
const leaguePrefixes = {
'nfl_scoreboard': 'nfl',
'mlb': 'mlb',
'milb': 'milb',
'nhl_scoreboard': 'nhl',
'nba_scoreboard': 'nba',
'ncaa_fb_scoreboard': 'ncaa_fb',
'ncaa_baseball_scoreboard': 'ncaa_baseball',
'ncaam_basketball_scoreboard': 'ncaam_basketball',
'soccer_scoreboard': 'soccer'
};
const p = leaguePrefixes[key] || key;
const liveModeEnabled = document.querySelector(`.sp-display-mode[data-league="${key}"][data-mode="live"]`)?.checked || false;
const recentModeEnabled = document.querySelector(`.sp-display-mode[data-league="${key}"][data-mode="recent"]`)?.checked || false;
const upcomingModeEnabled = document.querySelector(`.sp-display-mode[data-league="${key}"][data-mode="upcoming"]`)?.checked || false;
fragment[key] = {
enabled,
live_priority: livePriority,
@@ -3601,7 +3729,12 @@
recent_update_interval: recentUpd,
upcoming_update_interval: upcomingUpd,
recent_games_to_show: recentCount,
upcoming_games_to_show: upcomingCount
upcoming_games_to_show: upcomingCount,
display_modes: {
[`${p}_live`]: liveModeEnabled,
[`${p}_recent`]: recentModeEnabled,
[`${p}_upcoming`]: upcomingModeEnabled
}
};
});
await saveConfigJson(fragment);