From 9c4aeb3134d4c70290e48a9eeee199f2705327fc Mon Sep 17 00:00:00 2001 From: Yiannis Christodoulou Date: Thu, 9 Oct 2025 15:42:48 +0300 Subject: [PATCH] Refactor player controls and progress bar layout Moved native controls and progress bar positioning logic to PlayerConfig for better configurability. Separated control bar styling from progress bar layout, allowing independent application of styles. Added a spacer to the control bar to improve button alignment when the progress bar is repositioned. Cleaned up legacy code and improved maintainability. --- .../controls/AutoplayToggleButton.js | 4 +- .../components/video-player/VideoJSPlayer.jsx | 246 ++++++++++-------- .../video-js/src/config/playerConfig.js | 6 +- 3 files changed, 146 insertions(+), 110 deletions(-) diff --git a/frontend-tools/video-js/src/components/controls/AutoplayToggleButton.js b/frontend-tools/video-js/src/components/controls/AutoplayToggleButton.js index 932d4b78..79bf1cea 100644 --- a/frontend-tools/video-js/src/components/controls/AutoplayToggleButton.js +++ b/frontend-tools/video-js/src/components/controls/AutoplayToggleButton.js @@ -78,7 +78,7 @@ class AutoplayToggleButton extends Button { // Add appropriate icon class based on state if (this.isAutoplayEnabled) { // this.iconSpan.classList.add('vjs-icon-spinner'); - this.iconSpan.innerHTML = ` + this.iconSpan.innerHTML = `12321321 @@ -89,7 +89,7 @@ class AutoplayToggleButton extends Button { `; } else { // this.iconSpan.classList.add('vjs-icon-play-circle'); - this.iconSpan.innerHTML = ` + this.iconSpan.innerHTML = `1232132 diff --git a/frontend-tools/video-js/src/components/video-player/VideoJSPlayer.jsx b/frontend-tools/video-js/src/components/video-player/VideoJSPlayer.jsx index ec689cd6..24354132 100644 --- a/frontend-tools/video-js/src/components/video-player/VideoJSPlayer.jsx +++ b/frontend-tools/video-js/src/components/video-player/VideoJSPlayer.jsx @@ -1831,10 +1831,6 @@ function VideoJSPlayer({ videoId = 'default-video' }) { return; } - const useNative = false; // /iPad|iPhone|iPod|Android/.test(navigator.userAgent); - console.log('useNative', useNative); - console.log('navigator.userAgent', navigator.userAgent); - //const timer = setTimeout(() => { // Double-check that we still don't have a player and element exists if (!playerRef.current && videoRef.current && !videoRef.current.player) { @@ -1942,7 +1938,7 @@ function VideoJSPlayer({ videoId = 'default-video' }) { }, // Force native controls for touch devices - nativeControlsForTouch: useNative, //true, + nativeControlsForTouch: PlayerConfig.nativeControlsForTouch, // Ensures consistent autoplay behavior across browsers (prevents unexpected blocking or auto-play issues) normalizeAutoplay: true, @@ -2093,7 +2089,7 @@ function VideoJSPlayer({ videoId = 'default-video' }) { // ===== HTML5 TECH OPTIONS ===== html5: { // Force native controls for touch devices - nativeControlsForTouch: useNative, //true, + nativeControlsForTouch: PlayerConfig.nativeControlsForTouch, // Use native audio tracks instead of emulated - disabled for consistency nativeAudioTracks: true, @@ -2502,57 +2498,13 @@ function VideoJSPlayer({ videoId = 'default-video' }) { const progressControl = controlBar.getChild('progressControl'); const seekBar = progressControl?.getChild('seekBar'); - // BEGIN: Move progress bar below control bar (native touch style) + // BEGIN: Apply control bar styling from config (always applied) setTimeout(() => { - const controlBar = playerRef.current.getChild('controlBar'); - const progressControl = controlBar?.getChild('progressControl'); - - if (progressControl && progressControl.el() && controlBar && controlBar.el()) { - const progressEl = progressControl.el(); - const controlBarEl = controlBar.el(); - controlBarEl.style.gap = 0; - - // Remove progress control from control bar - controlBar.removeChild(progressControl); - - // Create a wrapper div that will hold both progress and control bar - const wrapper = document.createElement('div'); - wrapper.className = 'vjs-controls-wrapper'; - wrapper.style.position = 'absolute'; - wrapper.style.bottom = '0'; - wrapper.style.left = '0'; - wrapper.style.right = '0'; - wrapper.style.width = '100%'; - - // Insert wrapper before control bar - controlBarEl.parentNode.insertBefore(wrapper, controlBarEl); - - // Position elements based on config - if (PlayerConfig.progressBar.position === 'top') { - // Progress bar above control bar - wrapper.appendChild(progressEl); - wrapper.appendChild(controlBarEl); - } else { - // Progress bar below control bar (default/native style) - wrapper.appendChild(controlBarEl); - wrapper.appendChild(progressEl); - } - - // Style the progress control using config values - progressEl.style.position = 'relative'; - progressEl.style.width = '100%'; - progressEl.style.height = '15px'; - progressEl.style.margin = '8px 0'; // Add top and bottom margin - progressEl.style.padding = '5px 10px'; // Add left and right padding/gap - progressEl.style.display = 'block'; - progressEl.style.transition = 'opacity 0.3s, visibility 0.3s'; // Smooth transition - progressEl.style.boxSizing = 'border-box'; // Ensure padding doesn't increase width - + const controlBarEl = controlBar?.el(); + if (controlBarEl) { // Style control bar using config values - controlBarEl.style.position = 'relative'; - controlBarEl.style.width = '100%'; controlBarEl.style.height = `${PlayerConfig.controlBar.height}em`; - controlBarEl.style.fontSize = `${PlayerConfig.controlBar.fontSize}em`; + controlBarEl.style.fontSize = `${PlayerConfig.controlBar.fontSize}px`; controlBarEl.style.backgroundColor = PlayerConfig.controlBar.backgroundColor; // Apply same line height to time-related controls @@ -2560,60 +2512,113 @@ function VideoJSPlayer({ videoId = 'default-video' }) { timeControls.forEach((timeControl) => { timeControl.style.lineHeight = `${PlayerConfig.controlBar.height}em`; }); - - // Style the progress holder and bars with config colors - const progressHolder = progressEl.querySelector('.vjs-progress-holder'); - if (progressHolder) { - progressHolder.style.height = '100%'; - progressHolder.style.margin = '0'; - progressHolder.style.backgroundColor = PlayerConfig.progressBar.trackColor; - } - - // Style the play progress bar (the filled part) - const playProgress = progressEl.querySelector('.vjs-play-progress'); - if (playProgress) { - playProgress.style.backgroundColor = PlayerConfig.progressBar.color; - } - - // Style the load progress bar (buffered part) - const loadProgress = progressEl.querySelector('.vjs-load-progress'); - if (loadProgress) { - loadProgress.style.backgroundColor = PlayerConfig.progressBar.bufferColor; - } - - // Store reference for cleanup - customComponents.current.movedProgressControl = progressControl; - customComponents.current.controlsWrapper = wrapper; - - // Hide/show progress bar with control bar based on user activity - const syncProgressVisibility = () => { - const isControlBarVisible = - controlBar.hasClass('vjs-visible') || - !playerRef.current.hasClass('vjs-user-inactive'); - - if (isControlBarVisible) { - progressEl.style.opacity = '1'; - progressEl.style.visibility = 'visible'; - } else { - progressEl.style.opacity = '0'; - progressEl.style.visibility = 'hidden'; - } - }; - - // Listen to user activity events - playerRef.current.on('useractive', syncProgressVisibility); - playerRef.current.on('userinactive', syncProgressVisibility); - - // Initial sync - syncProgressVisibility(); - - // Store cleanup function - customComponents.current.cleanupProgressVisibility = () => { - playerRef.current.off('useractive', syncProgressVisibility); - playerRef.current.off('userinactive', syncProgressVisibility); - }; } }, 100); + // END: Apply control bar styling from config + + // BEGIN: Move progress bar below control bar (native touch style) + if (PlayerConfig.progressBar.position === 'bottom' || PlayerConfig.progressBar.position === 'top') { + setTimeout(() => { + if (progressControl && progressControl.el() && controlBar && controlBar.el()) { + const progressEl = progressControl.el(); + const controlBarEl = controlBar.el(); + controlBarEl.style.gap = 0; + + // Remove progress control from control bar + controlBar.removeChild(progressControl); + + // Create a wrapper div that will hold both progress and control bar + const wrapper = document.createElement('div'); + wrapper.className = 'vjs-controls-wrapper'; + wrapper.style.position = 'absolute'; + wrapper.style.bottom = '0'; + wrapper.style.left = '0'; + wrapper.style.right = '0'; + wrapper.style.width = '100%'; + + // Insert wrapper before control bar + controlBarEl.parentNode.insertBefore(wrapper, controlBarEl); + + // Position elements based on config + if (PlayerConfig.progressBar.position === 'top') { + // Progress bar above control bar + wrapper.appendChild(progressEl); + wrapper.appendChild(controlBarEl); + } else { + // Progress bar below control bar (default/native style) + wrapper.appendChild(controlBarEl); + wrapper.appendChild(progressEl); + } + + // Style the progress control using config values + progressEl.style.position = 'relative'; + progressEl.style.width = '100%'; + progressEl.style.height = '15px'; + progressEl.style.margin = '8px 0'; // Add top and bottom margin + progressEl.style.padding = '5px 10px'; // Add left and right padding/gap + progressEl.style.display = 'block'; + progressEl.style.transition = 'opacity 0.3s, visibility 0.3s'; // Smooth transition + progressEl.style.boxSizing = 'border-box'; // Ensure padding doesn't increase width + + // Style control bar positioning + controlBarEl.style.position = 'relative'; + controlBarEl.style.width = '100%'; + + // Style the progress holder and bars with config colors + const progressHolder = progressEl.querySelector('.vjs-progress-holder'); + if (progressHolder) { + progressHolder.style.height = '100%'; + progressHolder.style.margin = '0'; + progressHolder.style.backgroundColor = PlayerConfig.progressBar.trackColor; + } + + // Style the play progress bar (the filled part) + const playProgress = progressEl.querySelector('.vjs-play-progress'); + if (playProgress) { + playProgress.style.backgroundColor = PlayerConfig.progressBar.color; + } + + // Style the load progress bar (buffered part) + const loadProgress = progressEl.querySelector('.vjs-load-progress'); + if (loadProgress) { + loadProgress.style.backgroundColor = PlayerConfig.progressBar.bufferColor; + } + + // Store reference for cleanup + customComponents.current.movedProgressControl = progressControl; + customComponents.current.controlsWrapper = wrapper; + + // Hide/show progress bar with control bar based on user activity + const syncProgressVisibility = () => { + const isControlBarVisible = + controlBar.hasClass('vjs-visible') || + !playerRef.current.hasClass('vjs-user-inactive'); + + if (isControlBarVisible) { + progressEl.style.opacity = '1'; + progressEl.style.visibility = 'visible'; + } else { + progressEl.style.opacity = '0'; + progressEl.style.visibility = 'hidden'; + } + }; + + // Listen to user activity events + playerRef.current.on('useractive', syncProgressVisibility); + playerRef.current.on('userinactive', syncProgressVisibility); + + // Initial sync + syncProgressVisibility(); + + // Store cleanup function + customComponents.current.cleanupProgressVisibility = () => { + playerRef.current.off('useractive', syncProgressVisibility); + playerRef.current.off('userinactive', syncProgressVisibility); + }; + } + }, 100); + } + // END: Move progress bar below control bar // Debug: Check if progress control exists and is visible on touch devices @@ -2791,6 +2796,35 @@ function VideoJSPlayer({ videoId = 'default-video' }) { customComponents.current.customRemainingTime = customRemainingTime; // END: Implement custom time display component + // BEGIN: Add spacer to push right-side buttons to the right + if ( + controlBar && + customRemainingTime && + customRemainingTime.el() && + (PlayerConfig.progressBar.position === 'top' || PlayerConfig.progressBar.position === 'bottom') + ) { + // Create spacer element + const spacer = document.createElement('div'); + spacer.className = 'vjs-spacer-control vjs-control'; + spacer.style.flex = '1'; + spacer.style.minWidth = '1px'; + + // Insert spacer right after the time display + const controlBarEl = controlBar.el(); + const timeDisplayEl = customRemainingTime.el(); + const nextSibling = timeDisplayEl.nextSibling; + + if (nextSibling) { + controlBarEl.insertBefore(spacer, nextSibling); + } else { + controlBarEl.appendChild(spacer); + } + + // Store reference for cleanup + customComponents.current.spacer = spacer; + } + // END: Add spacer + // BEGIN: Wrap volume panel in custom div container /* setTimeout(() => { const volumePanel = controlBar.getChild('volumePanel'); diff --git a/frontend-tools/video-js/src/config/playerConfig.js b/frontend-tools/video-js/src/config/playerConfig.js index 645ede7b..4ab77b90 100644 --- a/frontend-tools/video-js/src/config/playerConfig.js +++ b/frontend-tools/video-js/src/config/playerConfig.js @@ -4,12 +4,14 @@ */ const PlayerConfig = { + nativeControlsForTouch: true, + // Progress bar configuration progressBar: { // Position: 'top' or 'bottom' // 'top' - progress bar above control bar // 'bottom' - progress bar below control bar (default/native style) - position: 'top', + position: 'default', // Progress bar color (hex, rgb, or CSS color name) color: '#019932', @@ -30,7 +32,7 @@ const PlayerConfig = { height: 3, // Font size in em units - fontSize: 1.5, + fontSize: 26, }, };