fix: mobile epg programme alignment
This commit is contained in:
+13
-9
@@ -989,10 +989,22 @@ video::cue {
|
|||||||
|
|
||||||
.epg-time-header {
|
.epg-time-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
background: var(--color-bg-secondary);
|
background: var(--color-bg-secondary);
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
margin-left: var(--epg-sidebar-width);
|
}
|
||||||
|
|
||||||
|
.epg-header-corner {
|
||||||
|
width: var(--epg-sidebar-width);
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
border-right: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epg-time-slots {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.epg-time-slot {
|
.epg-time-slot {
|
||||||
@@ -2045,14 +2057,6 @@ kbd {
|
|||||||
width: var(--epg-sidebar-width);
|
width: var(--epg-sidebar-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
.epg-time-header {
|
|
||||||
margin-left: var(--epg-sidebar-width);
|
|
||||||
}
|
|
||||||
|
|
||||||
.epg-time-slot {
|
|
||||||
min-width: 160px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Movies & Series */
|
/* Movies & Series */
|
||||||
.movies-header,
|
.movies-header,
|
||||||
.series-header {
|
.series-header {
|
||||||
|
|||||||
@@ -429,17 +429,20 @@ class EpgGuide {
|
|||||||
// Calculate total height for virtual scrolling
|
// Calculate total height for virtual scrolling
|
||||||
const totalHeight = this.filteredChannels.length * this.rowHeight;
|
const totalHeight = this.filteredChannels.length * this.rowHeight;
|
||||||
|
|
||||||
// Build HTML structure with virtual scroll container
|
// Build HTML structure - header is INSIDE scroll container for natural sync
|
||||||
this.container.innerHTML = `
|
this.container.innerHTML = `
|
||||||
<div class="epg-container" style="position: relative;">
|
<div class="epg-container" style="position: relative;">
|
||||||
<div class="epg-time-header">
|
|
||||||
${timeSlots.map(slot => `
|
|
||||||
<div class="epg-time-slot" style="width: ${30 * this.pixelsPerMinute}px;">
|
|
||||||
${slot.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
|
||||||
</div>
|
|
||||||
`).join('')}
|
|
||||||
</div>
|
|
||||||
<div class="epg-scroll-container" style="overflow: auto; max-height: calc(100vh - 200px);">
|
<div class="epg-scroll-container" style="overflow: auto; max-height: calc(100vh - 200px);">
|
||||||
|
<div class="epg-time-header">
|
||||||
|
<div class="epg-header-corner"></div>
|
||||||
|
<div class="epg-time-slots">
|
||||||
|
${timeSlots.map(slot => `
|
||||||
|
<div class="epg-time-slot" style="width: ${30 * this.pixelsPerMinute}px;">
|
||||||
|
${slot.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
||||||
|
</div>
|
||||||
|
`).join('')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="epg-spacer" style="height: ${totalHeight}px; position: relative;">
|
<div class="epg-spacer" style="height: ${totalHeight}px; position: relative;">
|
||||||
<div class="epg-channel-rows" style="position: absolute; top: 0; left: 0; right: 0;"></div>
|
<div class="epg-channel-rows" style="position: absolute; top: 0; left: 0; right: 0;"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -465,28 +468,38 @@ class EpgGuide {
|
|||||||
this.scrollContainer?.removeEventListener('scroll', this._hScrollHandler);
|
this.scrollContainer?.removeEventListener('scroll', this._hScrollHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get reference to time header for horizontal scroll sync
|
// Get reference to time slots container for horizontal scroll sync
|
||||||
this.timeHeader = this.container.querySelector('.epg-time-header');
|
this.timeSlotsContainer = this.container.querySelector('.epg-time-slots');
|
||||||
|
|
||||||
// Set up scroll handler for virtual scrolling (debounced for performance)
|
// Set up scroll handler for virtual scrolling (debounced for performance)
|
||||||
this._scrollHandler = this.debounce(() => this.updateVisibleRows(), 16); // ~60fps
|
this._scrollHandler = this.debounce(() => this.updateVisibleRows(), 16); // ~60fps
|
||||||
this.scrollContainer.addEventListener('scroll', this._scrollHandler);
|
this.scrollContainer.addEventListener('scroll', this._scrollHandler);
|
||||||
|
|
||||||
// Set up horizontal scroll sync (instant, not debounced)
|
// No need for horizontal scroll sync anymore - header is inside scroll container
|
||||||
this._hScrollHandler = () => {
|
|
||||||
if (this.timeHeader && this.scrollContainer) {
|
|
||||||
this.timeHeader.style.transform = `translateX(-${this.scrollContainer.scrollLeft}px)`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.scrollContainer.addEventListener('scroll', this._hScrollHandler);
|
|
||||||
|
|
||||||
// Initial render of visible rows
|
// Initial render of visible rows
|
||||||
this.updateVisibleRows();
|
this.updateVisibleRows();
|
||||||
|
|
||||||
|
// Sync header corner width with actual sidebar width (handles CSS overrides)
|
||||||
|
this.syncHeaderCornerWidth();
|
||||||
|
|
||||||
// Add now indicator
|
// Add now indicator
|
||||||
this.updateNowIndicator();
|
this.updateNowIndicator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync header corner width with actual sidebar width
|
||||||
|
* This handles cases where CSS variables are overridden on mobile
|
||||||
|
*/
|
||||||
|
syncHeaderCornerWidth() {
|
||||||
|
const sidebar = this.container.querySelector('.epg-channel-info');
|
||||||
|
const headerCorner = this.container.querySelector('.epg-header-corner');
|
||||||
|
if (sidebar && headerCorner) {
|
||||||
|
const actualWidth = sidebar.offsetWidth;
|
||||||
|
headerCorner.style.width = `${actualWidth}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update visible rows based on scroll position (Virtual Scrolling)
|
* Update visible rows based on scroll position (Virtual Scrolling)
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user