From 82482b87784515bbb698ca2ec46e2f2b45f23788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Ml=C3=A1dek?= Date: Thu, 22 May 2025 23:51:14 +0200 Subject: [PATCH] add a toggle for maximizing videos with numbers on the keyboard --- index.html | 46 +++++++++++++++++++++++ src/main.ts | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 149 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 7d7d31d..348e41e 100644 --- a/index.html +++ b/index.html @@ -45,6 +45,52 @@ grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; } + + /* Maximized view layout */ + .video-container.maximized { + display: flex; + flex-direction: row; + } + + .video-container.maximized .main-video { + width: 80%; + height: 100%; + } + + .video-container.maximized .side-videos { + width: 20%; + height: 100%; + display: flex; + flex-direction: column; + overflow-y: auto; + } + + .video-container.maximized .side-videos .video-wrapper { + position: relative; + height: 150px; + min-height: 150px; + width: 100%; + margin-bottom: 5px; + } + + .video-container.maximized .side-videos video { + height: 100%; + width: 100%; + } + + .video-number-label { + font-family: "Helvetica", "Arial", sans-serif; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 48px; + font-weight: bold; + color: white; + z-index: 10; + mix-blend-mode: difference; + } + video { width: 100%; height: 100%; diff --git a/src/main.ts b/src/main.ts index d0f0be5..9a0a21f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,6 +7,8 @@ type VideoConfig = { let videos: VideoConfig[] = []; let videoElements: HTMLVideoElement[] = []; +let isMaximizedView = false; +let maximizedVideoIndex = -1; // Initialize the video grid async function initialize(url: string) { @@ -204,12 +206,110 @@ function onTimeUpdate(event: Event) { } } +// Function to toggle maximized view for a specific video +function toggleMaximizedView(index: number) { + const container = document.getElementById("videoContainer"); + if (!container) return; + + // If we're already in maximized view and trying to maximize the same video, do nothing + if (isMaximizedView && maximizedVideoIndex === index) return; + + // If we're trying to restore grid view (index === -1) + if (index === -1) { + if (!isMaximizedView) return; // Already in grid view + + // Remove maximized class and restore grid class + container.classList.remove("maximized"); + container.classList.add(`count-${videos.length}`); + + // Remove any wrapper divs and restore original structure + while (container.firstChild) { + container.removeChild(container.firstChild); + } + + // Re-add all videos directly to the container + videoElements.forEach(video => { + container.appendChild(video); + }); + + isMaximizedView = false; + maximizedVideoIndex = -1; + return; + } + + // If the index is out of range, do nothing + if (index < 0 || index >= videoElements.length) return; + + // Switch to maximized view + container.classList.remove(`count-${videos.length}`); + container.classList.add("maximized"); + + // Clear the container + while (container.firstChild) { + container.removeChild(container.firstChild); + } + + // Create main video div + const mainVideoDiv = document.createElement("div"); + mainVideoDiv.className = "main-video"; + mainVideoDiv.appendChild(videoElements[index]); + container.appendChild(mainVideoDiv); + + // Create side videos div + const sideVideosDiv = document.createElement("div"); + sideVideosDiv.className = "side-videos"; + container.appendChild(sideVideosDiv); + + // Add all other videos to side videos div with number labels + videoElements.forEach((video, i) => { + if (i !== index) { + // Create a wrapper for the video and label + const videoWrapper = document.createElement("div"); + videoWrapper.className = "video-wrapper"; + + // Add the video to the wrapper + videoWrapper.appendChild(video); + + // Create and add the number label + const numberLabel = document.createElement("div"); + numberLabel.className = "video-number-label"; + numberLabel.textContent = String(i + 1); // 1-based indexing for display + videoWrapper.appendChild(numberLabel); + + // Add the wrapper to the side videos div + sideVideosDiv.appendChild(videoWrapper); + } + }); + + isMaximizedView = true; + maximizedVideoIndex = index; +} + +// Handle keyboard events +function handleKeyPress(event: KeyboardEvent) { + // Check if the key pressed is a number + const key = event.key; + if (/^[0-9]$/.test(key)) { + const num = parseInt(key, 10); + if (num === 0) { + // Restore grid view + toggleMaximizedView(-1); + } else if (num <= videoElements.length) { + // Maximize the corresponding video (1-based indexing for user, 0-based for array) + toggleMaximizedView(num - 1); + } + } +} + // Initialize everything when the page loads document.addEventListener("DOMContentLoaded", async () => { try { - initialize("content/data.json"); + await initialize("content/data.json"); + + // Add keyboard event listener + document.addEventListener("keydown", handleKeyPress); } catch (err) { - console.error(err); - alert(`Failed to load videos: ${err}`); + console.error("Failed to initialize:", err); + alert(`Failed to initialize: ${err}`); } });