From 86729dab5a9b59c1f759382e451758b9de830c5d Mon Sep 17 00:00:00 2001 From: Johnnybegood90 Date: Mon, 9 Mar 2026 16:18:54 +0100 Subject: [PATCH] Fix now-line alignment and visibility --- index.php | 131 ++++++++++++++++++++++++++---------------------------- 1 file changed, 62 insertions(+), 69 deletions(-) diff --git a/index.php b/index.php index 87ade36..831ccd4 100644 --- a/index.php +++ b/index.php @@ -256,27 +256,34 @@ $m3u_url = htmlspecialchars($config["m3u_url"] ?? ""); .timeline-area { flex: 1; - overflow: hidden; - display: flex; - flex-direction: column; + overflow: auto; position: relative; min-height: 0; + scrollbar-width: thin; + scrollbar-color: var(--border-bright) transparent; + } + .timeline-area::-webkit-scrollbar { height: 4px; width: 4px; } + .timeline-area::-webkit-scrollbar-track { background: transparent; } + .timeline-area::-webkit-scrollbar-thumb { background: var(--border-bright); border-radius: 2px; } + + /* Conteneur unique — ruler + programmes scrollent ensemble */ + .timeline-inner { + position: relative; + display: block; + min-width: 100%; } .time-ruler { height: var(--header-h); background: var(--surface); border-bottom: 1px solid var(--border-bright); - position: relative; - flex-shrink: 0; - overflow: hidden; + position: sticky; + top: 0; + z-index: 20; } .time-ruler-inner { - position: absolute; - top: 0; left: 0; + position: relative; height: 100%; - display: flex; - align-items: flex-end; } .time-tick { position: absolute; @@ -299,8 +306,9 @@ $m3u_url = htmlspecialchars($config["m3u_url"] ?? ""); .now-line { position: absolute; - top: 0; bottom: 0; + top: 0; width: 2px; + height: 100%; background: var(--accent-live); z-index: 50; pointer-events: none; @@ -314,25 +322,14 @@ $m3u_url = htmlspecialchars($config["m3u_url"] ?? ""); border-radius: 50%; } - .programs-scroll { - flex: 1; - overflow: auto; - scrollbar-width: thin; - scrollbar-color: var(--border-bright) transparent; + .programs-inner { position: relative; - min-height: 0; background-image: repeating-linear-gradient( to bottom, transparent 0px, transparent 79px, var(--border) 79px, var(--border) 80px ); - background-attachment: local; } - .programs-scroll::-webkit-scrollbar { height: 4px; width: 4px; } - .programs-scroll::-webkit-scrollbar-track { background: transparent; } - .programs-scroll::-webkit-scrollbar-thumb { background: var(--border-bright); border-radius: 2px; } - - .programs-inner { position: relative; } .program-row { height: var(--row-h); @@ -655,14 +652,11 @@ $m3u_url = htmlspecialchars($config["m3u_url"] ?? "");
-
-
-
-
-
-
-
+
+
+
+
@@ -674,15 +668,21 @@ $m3u_url = htmlspecialchars($config["m3u_url"] ?? ""); const DEFAULT_EPG_URL = ''; const DEFAULT_M3U_URL = ''; const PX_PER_MIN = 5; -const HOURS_BEFORE = 1; -const HOURS_AFTER = 3; +const GRID_HOURS = 72; const CORS_PROXY = 'https://corsproxy.io/?'; +// Ancre : 2h avant maintenant, arrondi au quart d'heure inférieur +const _d = new Date(); +_d.setMinutes(Math.floor(_d.getMinutes()/15)*15, 0, 0); +const GRID_START = new Date(_d.getTime() - 2*3600000); +const GRID_END = new Date(GRID_START.getTime() + GRID_HOURS*3600000); +const TOTAL_WIDTH_PX = GRID_HOURS * 60 * PX_PER_MIN; + +function msToX(ms) { return (ms - GRID_START.getTime()) / 60000 * PX_PER_MIN; } + let channels = []; let programs = {}; -let viewOffsetMin = 0; -let totalWidthPx = 0; -let scrollListenerAdded = false; +let scrollListenerAdded = false; // sync scroll vertical chaînes function pad(n) { return String(n).padStart(2,'0'); } @@ -809,18 +809,12 @@ function parseAndRender(doc) { function renderAll() { if (isMobile()) { renderMobile(); } - else { renderGrid(); setTimeout(centerNow, 50); } + else { renderGrid(); setTimeout(centerNow, 150); } } // ── GRID ────────────────────────────────────────────────────────────────────── -function getViewStart() { return new Date(Date.now() - HOURS_BEFORE*3600000 + viewOffsetMin*60000); } -function getViewEnd() { return new Date(Date.now() + HOURS_AFTER*3600000 + viewOffsetMin*60000); } -function msToX(ms) { return (ms - getViewStart().getTime()) / 60000 * PX_PER_MIN; } - function renderGrid() { - const vs = getViewStart(), ve = getViewEnd(); - totalWidthPx = (ve - vs) / 60000 * PX_PER_MIN; - renderChannels(); renderRuler(vs, ve); renderPrograms(vs, ve); positionNowLine(); + renderChannels(); renderRuler(); renderPrograms(); positionNowLine(); } function makePlaceholder(name) { @@ -849,12 +843,13 @@ function renderChannels() { }); } -function renderRuler(vs, ve) { +function renderRuler() { const ruler = document.getElementById('timeRulerInner'); - ruler.innerHTML = ''; ruler.style.width = totalWidthPx + 'px'; - let t = new Date(vs); t.setSeconds(0,0); + ruler.innerHTML = ''; ruler.style.width = TOTAL_WIDTH_PX + 'px'; + document.getElementById('timeRuler').style.width = TOTAL_WIDTH_PX + 'px'; + let t = new Date(GRID_START); t.setSeconds(0,0); const rem = t.getMinutes()%15; if (rem) t.setMinutes(t.getMinutes()+(15-rem)); - while (t <= ve) { + while (t <= GRID_END) { const x = msToX(t.getTime()); const isHour = t.getMinutes()===0; const tick = document.createElement('div'); tick.className='time-tick'; tick.style.left=x+'px'; const lbl = document.createElement('div'); lbl.className='time-tick-label'+(isHour?' hour':''); lbl.textContent=fmtTime(t); @@ -864,10 +859,19 @@ function renderRuler(vs, ve) { } } -function renderPrograms(vs, ve) { +function renderPrograms() { const ROW_H = 80; const inner = document.getElementById('programsInner'); - inner.innerHTML = ''; inner.style.width = totalWidthPx+'px'; inner.style.height = (channels.length*ROW_H)+'px'; + const totalH = channels.length * ROW_H; + inner.innerHTML = ''; + inner.style.width = TOTAL_WIDTH_PX+'px'; + inner.style.height = totalH+'px'; + + // Remettre la now-line (détruite par innerHTML='') + const nowLine = document.createElement('div'); + nowLine.className = 'now-line'; nowLine.id = 'nowLine'; + nowLine.style.height = totalH + 'px'; + inner.appendChild(nowLine); const now = new Date(); @@ -876,9 +880,9 @@ function renderPrograms(vs, ve) { row.className = 'program-row'; row.style.cssText = `top:${i*ROW_H}px;position:absolute;left:0;right:0;`; - (programs[ch.id]||[]).filter(p => p.stop>vs && p.start { + (programs[ch.id]||[]).filter(p => p.stop>GRID_START && p.start { const x = Math.max(0, msToX(p.start.getTime())); - const w = Math.min(totalWidthPx, msToX(p.stop.getTime())) - x; + const w = Math.min(TOTAL_WIDTH_PX, msToX(p.stop.getTime())) - x; if (w < 2) return; const block = document.createElement('div'); block.className='program-block'; if (p.stop < now) block.classList.add('is-past'); @@ -905,11 +909,7 @@ function renderPrograms(vs, ve) { positionNowLine(); if (!scrollListenerAdded) { scrollListenerAdded = true; - document.getElementById('programsScroll').addEventListener('scroll', function() { - document.getElementById('timeRulerInner').style.transform = `translateX(-${this.scrollLeft}px)`; - const visibleX = msToX(Date.now()) - this.scrollLeft; - document.getElementById('nowLineRuler').style.left = visibleX + 'px'; - document.getElementById('nowLineGrid').style.left = visibleX + 'px'; + document.getElementById('timelineArea').addEventListener('scroll', function() { document.getElementById('channelsList').scrollTop = this.scrollTop; }); } @@ -917,26 +917,19 @@ function renderPrograms(vs, ve) { function positionNowLine() { const x = msToX(Date.now()); - const scroll = document.getElementById('programsScroll'); - const scrollLeft = scroll ? scroll.scrollLeft : 0; - const ruler = document.getElementById('nowLineRuler'); - const grid = document.getElementById('nowLineGrid'); - const visibleX = x - scrollLeft; - if (ruler) { ruler.style.left = visibleX + 'px'; ruler.style.top='0'; ruler.style.bottom='0'; } - if (grid) { grid.style.left = visibleX + 'px'; grid.style.top='0'; grid.style.height='100%'; } + const line = document.getElementById('nowLine'); + if (line) line.style.left = x + 'px'; } function centerNow() { - viewOffsetMin = 0; renderGrid(); - setTimeout(() => { - const scroll = document.getElementById('programsScroll'); - scroll.scrollLeft = Math.max(0, msToX(Date.now()) - document.getElementById('timelineArea').offsetWidth*0.3); - }, 50); + const area = document.getElementById('timelineArea'); + area.scrollLeft = Math.max(0, msToX(Date.now()) - area.offsetWidth * 0.3); document.querySelectorAll('.nav-btn').forEach((b,i) => b.classList.toggle('active', i===1)); } function shiftView(mins) { - viewOffsetMin += mins; renderGrid(); + const area = document.getElementById('timelineArea'); + area.scrollLeft = Math.max(0, area.scrollLeft + mins * PX_PER_MIN); document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active')); }