feat: implement fully responsive mobile design with channel drawer
- Add comprehensive responsive breakpoints (1024px, 768px, 640px, 480px) - Implement slide-out channel drawer for mobile Live TV page - Add floating 'Channels' button with overlay for mobile - Convert navigation to hamburger menu on small screens - Optimize all pages (Movies, Series, EPG, Settings) for mobile - Add touch-friendly interactions and safe area insets - Improve source action buttons layout on mobile - Add smooth scrolling and overscroll behavior controls - Implement proper visibility handling for mobile menus
This commit is contained in:
+902
-6
File diff suppressed because it is too large
Load Diff
+22
-3
@@ -3,7 +3,10 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<title>NodeCast TV | IPTV Player</title>
|
<title>NodeCast TV | IPTV Player</title>
|
||||||
<meta name="description" content="Stream live TV, movies, and series with EPG support">
|
<meta name="description" content="Stream live TV, movies, and series with EPG support">
|
||||||
<link rel="stylesheet" href="css/main.css">
|
<link rel="stylesheet" href="css/main.css">
|
||||||
@@ -25,7 +28,12 @@
|
|||||||
</svg></span>
|
</svg></span>
|
||||||
<span class="brand-text">NodeCast <span class="brand-accent">TV</span></span>
|
<span class="brand-text">NodeCast <span class="brand-accent">TV</span></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-menu">
|
<button class="mobile-menu-toggle" id="mobile-menu-toggle" aria-label="Toggle menu">
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
</button>
|
||||||
|
<div class="navbar-menu" id="navbar-menu">
|
||||||
<a href="#" class="nav-link active" data-page="home">
|
<a href="#" class="nav-link active" data-page="home">
|
||||||
<span class="nav-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
|
<span class="nav-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
|
||||||
class="icon">
|
class="icon">
|
||||||
@@ -73,8 +81,19 @@
|
|||||||
<!-- Home Page -->
|
<!-- Home Page -->
|
||||||
<div id="page-home" class="page active">
|
<div id="page-home" class="page active">
|
||||||
<div class="home-layout">
|
<div class="home-layout">
|
||||||
|
<!-- Channel Toggle Button (Mobile) -->
|
||||||
|
<button class="channel-toggle-btn" id="channel-toggle-btn">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="icon">
|
||||||
|
<path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z" />
|
||||||
|
</svg>
|
||||||
|
<span>Channels</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Overlay for drawer -->
|
||||||
|
<div class="channel-sidebar-overlay" id="channel-sidebar-overlay"></div>
|
||||||
|
|
||||||
<!-- Channel Sidebar -->
|
<!-- Channel Sidebar -->
|
||||||
<aside class="channel-sidebar">
|
<aside class="channel-sidebar" id="channel-sidebar">
|
||||||
<div class="sidebar-header">
|
<div class="sidebar-header">
|
||||||
<div class="search-wrapper">
|
<div class="search-wrapper">
|
||||||
<input type="text" id="channel-search" placeholder="Search channels..." class="search-input">
|
<input type="text" id="channel-search" placeholder="Search channels..." class="search-input">
|
||||||
|
|||||||
@@ -24,6 +24,59 @@ class App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
|
// Mobile menu toggle
|
||||||
|
const mobileMenuToggle = document.getElementById('mobile-menu-toggle');
|
||||||
|
const navbarMenu = document.getElementById('navbar-menu');
|
||||||
|
|
||||||
|
if (mobileMenuToggle && navbarMenu) {
|
||||||
|
mobileMenuToggle.addEventListener('click', () => {
|
||||||
|
mobileMenuToggle.classList.toggle('active');
|
||||||
|
navbarMenu.classList.toggle('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close menu when a nav link is clicked
|
||||||
|
document.querySelectorAll('.nav-link').forEach(link => {
|
||||||
|
link.addEventListener('click', () => {
|
||||||
|
mobileMenuToggle.classList.remove('active');
|
||||||
|
navbarMenu.classList.remove('active');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close menu when clicking outside
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
if (!e.target.closest('.navbar')) {
|
||||||
|
mobileMenuToggle.classList.remove('active');
|
||||||
|
navbarMenu.classList.remove('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channel drawer toggle (mobile)
|
||||||
|
const channelToggleBtn = document.getElementById('channel-toggle-btn');
|
||||||
|
const channelSidebar = document.getElementById('channel-sidebar');
|
||||||
|
const channelOverlay = document.getElementById('channel-sidebar-overlay');
|
||||||
|
|
||||||
|
if (channelToggleBtn && channelSidebar && channelOverlay) {
|
||||||
|
const toggleChannelDrawer = () => {
|
||||||
|
channelSidebar.classList.toggle('active');
|
||||||
|
channelOverlay.classList.toggle('active');
|
||||||
|
};
|
||||||
|
|
||||||
|
channelToggleBtn.addEventListener('click', toggleChannelDrawer);
|
||||||
|
channelOverlay.addEventListener('click', toggleChannelDrawer);
|
||||||
|
|
||||||
|
// Close drawer when a channel is selected
|
||||||
|
channelSidebar.addEventListener('click', (e) => {
|
||||||
|
if (e.target.closest('.channel-item')) {
|
||||||
|
// Small delay to let the channel selection happen
|
||||||
|
setTimeout(() => {
|
||||||
|
channelSidebar.classList.remove('active');
|
||||||
|
channelOverlay.classList.remove('active');
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Navigation handling
|
// Navigation handling
|
||||||
document.querySelectorAll('.nav-link').forEach(link => {
|
document.querySelectorAll('.nav-link').forEach(link => {
|
||||||
link.addEventListener('click', (e) => {
|
link.addEventListener('click', (e) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user