feat(info-overlay): add no-EPG layout and gradient blur

Introduce a separate no-EPG presentation and move backdrop blur
into an isolated pseudo-element to avoid visual bleed and improve
layout control.

- Replace direct backdrop-filter on the overlay with a ::before
  pseudo-element that applies backdrop-filter and mask-image
  gradients (with -webkit- prefixed fallbacks) for a smoother
  gradient blur effect.
- Add isolation on the overlay to contain stacking context and set
  z-index of the pseudo-element to -1 to keep content above the blur.
- Add .no-epg modifier on the overlay and new .no-epg-layout,
  .channel-logo-large and .channel-name-large styles to provide a
  centered, accessible layout when no EPG data is available.
- Update template to render two distinct layouts: one when
  epgProgram exists (original detailed view) and one fallback view
  when epgProgram is missing, including larger channel logo and
  centered channel name.

These changes improve visual fidelity of the blur effect and add a
clear fallback UI for channels without EPG data.
This commit is contained in:
4gray committed 2025-11-22 11:40:16 +01:00
1 parent 5796dd0a48
commit ce6aa1b3d1
2 files changed
+108 -28

No files matched your search

@@ -1,37 +1,58 @@
@if (channel && epgProgram) {
@if (channel) {
<div
class="channel-overlay"
[class.visible]="isVisible"
[class.hidden]="!isVisible"
[class.no-epg]="!epgProgram"
>
<div class="channel-logo">
<img
[src]="epgProgram?.icon?.[0]?.src || channel?.tvg?.logo"
width="68"
onerror="this.style.display='none'"
alt="channel icon"
/>
</div>
<div class="content">
<div class="program-name">
{{ epgProgram?.title?.[0]?.value }} | {{ channel.name }}
@if (epgProgram) {
<!-- Layout with EPG data -->
<div class="channel-logo">
<img
[src]="epgProgram?.icon?.[0]?.src || channel?.tvg?.logo"
width="68"
onerror="this.style.display='none'"
alt="channel icon"
/>
</div>
<div class="program-description">
{{ epgProgram?.desc?.[0]?.value }}
</div>
<div class="program-progress">
<div
class="program-finished"
[ngStyle]="{
width: (finishedDuration * 100) / generalDuration + '%',
}"
></div>
<div class="duration">
{{ start | momentDate: 'YYYYMMDDHHmm ZZ' : 'HH:mm' }}
-
{{ stop | momentDate: 'YYYYMMDDHHmm ZZ' : 'HH:mm' }}
<div class="content">
<div class="program-name">
{{ epgProgram?.title?.[0]?.value + ' |' }}
{{ channel.name }}
</div>
<div class="program-description">
{{ epgProgram?.desc?.[0]?.value }}
</div>
<div class="program-progress">
<div
class="program-finished"
[ngStyle]="{
width: (finishedDuration * 100) / generalDuration + '%',
}"
></div>
<div class="duration">
{{ start | momentDate: 'YYYYMMDDHHmm ZZ' : 'HH:mm' }}
-
{{ stop | momentDate: 'YYYYMMDDHHmm ZZ' : 'HH:mm' }}
</div>
</div>
</div>
</div>
} @else {
<!-- Layout without EPG data -->
<div class="no-epg-layout">
@if (channel?.tvg?.logo) {
<div class="channel-logo-large">
<img
[src]="channel.tvg.logo"
onerror="this.style.display='none'"
alt="channel icon"
/>
</div>
}
<div class="channel-name-large">
{{ channel.name }}
</div>
</div>
}
</div>
}
@@ -8,10 +8,30 @@
margin-right: auto;
left: 0;
right: 0;
backdrop-filter: blur(10px);
flex-direction: row;
box-sizing: border-box;
display: flex;
isolation: isolate;
// Gradient blur effect using pseudo-element
&::before {
content: '';
position: absolute;
inset: 0;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
mask-image: linear-gradient(to bottom, transparent 0%, rgba(0, 0, 0, 0.3) 20%, rgba(0, 0, 0, 0.8) 60%, black 100%);
-webkit-mask-image: linear-gradient(to bottom, transparent 0%, rgba(0, 0, 0, 0.3) 20%, rgba(0, 0, 0, 0.8) 60%, black 100%);
z-index: -1;
}
&.no-epg {
min-height: 100px;
max-height: 180px;
display: flex;
align-items: center;
justify-content: center;
}
}
.program-name,
@@ -85,3 +105,42 @@
opacity: 0;
transition: visibility 0s 2s, opacity 2s linear;
}
// No EPG layout styles
.no-epg-layout {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
padding: 20px;
gap: 20px;
}
.channel-logo-large {
display: flex;
align-items: center;
justify-content: center;
img {
max-width: 150px;
max-height: 100px;
width: auto;
height: auto;
object-fit: contain;
}
}
.channel-name-large {
font-size: 2.5rem;
font-weight: 600;
text-align: center;
line-height: 1.2;
max-width: 90%;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7);
}