Added camera enabling and disabling
This commit is contained in:
+246
-91
@@ -124,28 +124,21 @@
|
||||
height: calc(100vh - 50px);
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-template-rows: repeat(2, 1fr);
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
align-content: stretch;
|
||||
}
|
||||
|
||||
.camera-card {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.camera-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
@@ -189,12 +182,7 @@
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Calculate optimal size to fit within grid cell */
|
||||
@supports (width: min(100%, calc((100vh - 50px - 20px) / 2 - 60px) * 4 / 3)) {
|
||||
.stream-wrapper {
|
||||
width: min(100%, calc(((100vh - 50px - 20px) / 2 - 60px) * 4 / 3));
|
||||
}
|
||||
}
|
||||
/* stream-wrapper width is set dynamically via JS (reshapeGrid) */
|
||||
|
||||
.stream-placeholder {
|
||||
position: absolute;
|
||||
@@ -263,35 +251,7 @@
|
||||
background: linear-gradient(90deg, #999 0%, #999 60px, #1a1a1a 60px);
|
||||
}
|
||||
|
||||
.camera-card:hover .card-header {
|
||||
background: linear-gradient(90deg, #1e7e34 0%, #1e7e34 60px, #2d2d2d 60px);
|
||||
border-bottom-color: #28a745;
|
||||
}
|
||||
|
||||
/* Tooltip for camera cards */
|
||||
.camera-card::after {
|
||||
content: attr(data-tooltip);
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.85rem;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.2s ease;
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.camera-card:hover::after {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateX(-50%) translateY(-5px);
|
||||
}
|
||||
|
||||
/* Settings Panel */
|
||||
#overlay {
|
||||
@@ -491,6 +451,157 @@
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* Camera settings panel components */
|
||||
.cam-setting-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 0;
|
||||
font-size: 0.9rem;
|
||||
color: #495057;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.cam-setting-toggle input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
cursor: pointer;
|
||||
accent-color: #28a745;
|
||||
}
|
||||
|
||||
.cam-slider-row {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.cam-slider-label {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 0.85rem;
|
||||
color: #495057;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.cam-slider-value {
|
||||
font-weight: 600;
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.cam-slider {
|
||||
width: 100%;
|
||||
accent-color: #28a745;
|
||||
}
|
||||
|
||||
.cam-list {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.cam-row {
|
||||
border: 1.5px solid #dee2e6;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
background: white;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.cam-row--disabled {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.cam-row-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 12px;
|
||||
background: #f8f9fa;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.cam-row-label {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.cam-row .camera-input {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
padding: 10px 12px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.cam-row .camera-input:focus {
|
||||
border: none;
|
||||
box-shadow: inset 0 0 0 2px #28a745;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.cam-row .camera-input:hover:not(:focus) {
|
||||
border: none;
|
||||
box-shadow: inset 0 0 0 2px #28a74580;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* Toggle switch */
|
||||
.cam-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cam-toggle input[type="checkbox"] {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.cam-toggle-track {
|
||||
width: 36px;
|
||||
height: 20px;
|
||||
background: #dc3545;
|
||||
border-radius: 10px;
|
||||
position: relative;
|
||||
transition: background 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cam-toggle input:checked ~ .cam-toggle-track {
|
||||
background: #28a745;
|
||||
}
|
||||
|
||||
.cam-toggle-thumb {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.cam-toggle input:checked ~ .cam-toggle-track .cam-toggle-thumb {
|
||||
transform: translateX(16px);
|
||||
}
|
||||
|
||||
.cam-toggle-text {
|
||||
font-size: 0.78rem;
|
||||
font-weight: 600;
|
||||
color: #28a745;
|
||||
min-width: 22px;
|
||||
}
|
||||
|
||||
.cam-toggle-text--off {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.tournament-status-info {
|
||||
background: #f0f8ff;
|
||||
border: 2px solid #28a745;
|
||||
@@ -539,7 +650,6 @@
|
||||
.grid {
|
||||
height: calc(100vh - 100px);
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: repeat(6, 1fr);
|
||||
gap: 15px;
|
||||
padding: 20px;
|
||||
}
|
||||
@@ -549,9 +659,6 @@
|
||||
padding: 10px 12px 10px 0;
|
||||
}
|
||||
|
||||
.camera-card:hover .card-header {
|
||||
background: linear-gradient(90deg, #1e7e34 0%, #1e7e34 50px, #2d2d2d 50px);
|
||||
}
|
||||
|
||||
.target-number {
|
||||
width: 50px;
|
||||
@@ -611,9 +718,6 @@
|
||||
padding: 8px 10px 8px 0;
|
||||
}
|
||||
|
||||
.camera-card:hover .card-header {
|
||||
background: linear-gradient(90deg, #1e7e34 0%, #1e7e34 45px, #2d2d2d 45px);
|
||||
}
|
||||
|
||||
.target-number {
|
||||
width: 45px;
|
||||
@@ -719,46 +823,48 @@
|
||||
<div class="settings-group" id="displayOptionsGroup">
|
||||
<h4 data-i18n="league.camera">Kamera</h4>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: flex; align-items: center; gap: 8px; cursor: pointer;">
|
||||
<input type="checkbox" id="toggleTitles" {% if settings.display_options.show_titles %}checked{% endif %} onchange="toggleTitles()" style="width: 18px; height: 18px;">
|
||||
<span data-i18n="camera.show_card_titles">Prikaiž Naslove Kartic</span>
|
||||
</label>
|
||||
<!-- Display toggles -->
|
||||
<label class="cam-setting-toggle">
|
||||
<input type="checkbox" id="toggleTitles" {% if settings.display_options.show_titles %}checked{% endif %} onchange="toggleTitles()">
|
||||
<span data-i18n="camera.show_card_titles">Prikaži Naslove Kartic</span>
|
||||
</label>
|
||||
|
||||
<!-- Sliders -->
|
||||
<div class="cam-slider-row">
|
||||
<div class="cam-slider-label">
|
||||
<span data-i18n="camera.title_text_size">Naslov</span>
|
||||
<span class="cam-slider-value"><span id="titleSizeValue">{{ settings.display_options.title_size }}</span>rem</span>
|
||||
</div>
|
||||
<input type="range" id="titleSize" class="cam-slider" min="0.8" max="2.5" step="0.1"
|
||||
value="{{ settings.display_options.title_size }}" oninput="adjustTitleSize(this.value)">
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 8px; font-weight: bold; color: #333;">
|
||||
<span data-i18n="camera.title_text_size">Velikost Besedila Naslova</span>: <span id="titleSizeValue">{{ settings.display_options.title_size }}</span>rem
|
||||
</label>
|
||||
<input type="range"
|
||||
id="titleSize"
|
||||
min="0.8"
|
||||
max="2.5"
|
||||
step="0.1"
|
||||
value="{{ settings.display_options.title_size }}"
|
||||
oninput="adjustTitleSize(this.value)"
|
||||
style="width: 100%;">
|
||||
<div class="cam-slider-row">
|
||||
<div class="cam-slider-label">
|
||||
<span data-i18n="camera.target_number_size">Številka</span>
|
||||
<span class="cam-slider-value"><span id="targetNumberSizeValue">{{ settings.display_options.get('target_number_size', settings.display_options.title_size) }}</span>rem</span>
|
||||
</div>
|
||||
<input type="range" id="targetNumberSize" class="cam-slider" min="0.8" max="2.5" step="0.1"
|
||||
value="{{ settings.display_options.get('target_number_size', settings.display_options.title_size) }}" oninput="adjustTargetNumberSize(this.value)">
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 8px; font-weight: bold; color: #333;">
|
||||
<span data-i18n="camera.target_number_size">Velikost Števila Tarče</span>: <span id="targetNumberSizeValue">{{ settings.display_options.get('target_number_size', settings.display_options.title_size) }}</span>rem
|
||||
</label>
|
||||
<input type="range"
|
||||
id="targetNumberSize"
|
||||
min="0.8"
|
||||
max="2.5"
|
||||
step="0.1"
|
||||
value="{{ settings.display_options.get('target_number_size', settings.display_options.title_size) }}"
|
||||
oninput="adjustTargetNumberSize(this.value)"
|
||||
style="width: 100%;">
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 20px;">
|
||||
<h5 data-i18n="camera.titles">Naslovi Kamer</h5>
|
||||
<!-- Camera rows -->
|
||||
<div class="cam-list">
|
||||
{% for i in range(1, 7) %}
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 8px; font-weight: 500; color: #495057; font-size: 0.9rem;"><span data-i18n="camera.camera">Kamera</span> {{ i }}:</label>
|
||||
{% set is_disabled = i in settings.get('disabled_cameras', []) %}
|
||||
<div class="cam-row {% if is_disabled %}cam-row--disabled{% endif %}" id="cameraSettingsRow{{ i }}">
|
||||
<div class="cam-row-header">
|
||||
<span class="cam-row-label"><span data-i18n="camera.camera">Kamera</span> {{ i }}</span>
|
||||
<label class="cam-toggle" id="cameraToggleLabel{{ i }}">
|
||||
<input type="checkbox" id="cameraEnabled{{ i }}"
|
||||
{% if not is_disabled %}checked{% endif %}
|
||||
onchange="toggleCamera({{ i }}, this.checked)">
|
||||
<span class="cam-toggle-track">
|
||||
<span class="cam-toggle-thumb"></span>
|
||||
</span>
|
||||
<span class="cam-toggle-text {% if is_disabled %}cam-toggle-text--off{% endif %}" id="cameraStatusText{{ i }}">{% if is_disabled %}Off{% else %}On{% endif %}</span>
|
||||
</label>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="titleInput{{ i }}"
|
||||
value="{{ settings.camera_titles[i|string] }}"
|
||||
@@ -779,7 +885,8 @@
|
||||
|
||||
<div class="grid">
|
||||
{% for i in range(1, 7) %}
|
||||
<div class="camera-card" onclick="openCameraFullscreen({{ i }})" data-camera-id="{{ i }}" data-tooltip="" data-i18n-tooltip="camera.click_to_view_fullscreen">
|
||||
<div class="camera-card" data-camera-id="{{ i }}"
|
||||
{% if i in settings.get('disabled_cameras', []) %}style="display:none;"{% endif %}>
|
||||
<div class="stream-wrapper">
|
||||
<div class="card-header">
|
||||
<div class="target-number">{{ i }}</div>
|
||||
@@ -1174,6 +1281,58 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle camera enabled/disabled
|
||||
function reshapeGrid() {
|
||||
const grid = document.querySelector('.grid');
|
||||
if (!grid) return;
|
||||
const activeCount = document.querySelectorAll('.camera-card:not([style*="display: none"]):not([style*="display:none"])').length;
|
||||
let cols;
|
||||
if (activeCount <= 1) cols = 1;
|
||||
else if (activeCount === 2) cols = 2;
|
||||
else if (activeCount === 3) cols = 3;
|
||||
else if (activeCount === 4) cols = 2;
|
||||
else cols = 3;
|
||||
const rows = Math.ceil(activeCount / cols);
|
||||
grid.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
|
||||
|
||||
// Recompute stream-wrapper max-width so cards fill available height at 4:3
|
||||
// gap=20px, padding=20px each side, header~40px, toolbar=50px
|
||||
const gap = 20;
|
||||
const padding = 40;
|
||||
const toolbar = 50;
|
||||
const headerH = 40;
|
||||
const availH = (window.innerHeight - toolbar - padding - gap * (rows - 1)) / rows - headerH;
|
||||
const maxW = availH * (4 / 3);
|
||||
document.querySelectorAll('.stream-wrapper').forEach(w => {
|
||||
w.style.width = `min(100%, ${maxW}px)`;
|
||||
});
|
||||
}
|
||||
|
||||
function toggleCamera(index, enabled) {
|
||||
const card = document.querySelector(`.camera-card[data-camera-id="${index}"]`);
|
||||
const titleInput = document.getElementById(`titleInput${index}`);
|
||||
const label = document.getElementById(`cameraToggleLabel${index}`);
|
||||
const statusText = document.getElementById(`cameraStatusText${index}`);
|
||||
|
||||
const row = document.getElementById(`cameraSettingsRow${index}`);
|
||||
|
||||
if (enabled) {
|
||||
if (card) card.style.display = '';
|
||||
if (row) row.classList.remove('cam-row--disabled');
|
||||
if (statusText) { statusText.textContent = 'On'; statusText.classList.remove('cam-toggle-text--off'); }
|
||||
currentSettings.disabled_cameras = (currentSettings.disabled_cameras || []).filter(c => c !== index);
|
||||
} else {
|
||||
if (card) card.style.display = 'none';
|
||||
if (row) row.classList.add('cam-row--disabled');
|
||||
if (statusText) { statusText.textContent = 'Off'; statusText.classList.add('cam-toggle-text--off'); }
|
||||
if (!currentSettings.disabled_cameras) currentSettings.disabled_cameras = [];
|
||||
if (!currentSettings.disabled_cameras.includes(index)) currentSettings.disabled_cameras.push(index);
|
||||
}
|
||||
|
||||
reshapeGrid();
|
||||
saveSettings({ disabled_cameras: currentSettings.disabled_cameras });
|
||||
}
|
||||
|
||||
// Toggle titles
|
||||
function toggleTitles() {
|
||||
const show = document.getElementById('toggleTitles').checked;
|
||||
@@ -1236,12 +1395,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Open camera fullscreen
|
||||
function openCameraFullscreen(cameraId) {
|
||||
const customTitle = currentSettings.camera_titles[cameraId.toString()] || `Camera ${cameraId}`;
|
||||
window.location.href = `/fullscreen/${cameraId}?title=${encodeURIComponent(customTitle)}`;
|
||||
}
|
||||
|
||||
// Apply current settings
|
||||
function applyCurrentSettings() {
|
||||
// Apply title visibility
|
||||
@@ -1460,6 +1613,8 @@
|
||||
setInterval(updateDateTime, 1000);
|
||||
updateRoundNavigation();
|
||||
applyCurrentSettings();
|
||||
reshapeGrid();
|
||||
window.addEventListener('resize', reshapeGrid);
|
||||
addTouchFeedback();
|
||||
|
||||
// Initialize language selector after i18n is ready
|
||||
|
||||
Reference in New Issue
Block a user