mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-12 12:06:16 -08:00
fix(epg): close the programme popover when the window drops its block
Review follow-up (Codex): the ribbon window can remove a focused narrow block on scroll, and a removed node fires no focusout, so the fixed tooltip kept showing a programme no longer on screen. The popover is now a linkedSignal over the rendered items that keeps its state only while its block (by key) is still rendered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
623ffc672a
commit
923d5151eb
2 files changed
+61
-6
No files matched your search
@@ -82,11 +82,52 @@ describe('EpgTimelineTrackComponent', () => {
|
||||
expect(component.popover()).toBeNull();
|
||||
});
|
||||
|
||||
// The ribbon window can remove a focused block on scroll; a removed node
|
||||
// fires no focusout, so the popover must follow the rendered items.
|
||||
it('closes the popover when its block leaves the rendered items', () => {
|
||||
const item = renderBlock({ tier: 'narrow' });
|
||||
const other = renderBlock({ key: 'k2', tier: 'narrow' });
|
||||
fixture.componentRef.setInput('items', [item, other]);
|
||||
fixture.detectChanges();
|
||||
component.onBlockEnter(item, {
|
||||
currentTarget: {
|
||||
getBoundingClientRect: () => ({
|
||||
left: 100,
|
||||
width: 40,
|
||||
top: 60,
|
||||
bottom: 200,
|
||||
}),
|
||||
},
|
||||
} as unknown as Event);
|
||||
|
||||
fixture.componentRef.setInput('items', [item]);
|
||||
fixture.detectChanges();
|
||||
expect(component.popover()?.key).toBe('k1');
|
||||
expect(
|
||||
(fixture.nativeElement as HTMLElement).querySelector(
|
||||
'[role="tooltip"]'
|
||||
)
|
||||
).not.toBeNull();
|
||||
|
||||
fixture.componentRef.setInput('items', [other]);
|
||||
fixture.detectChanges();
|
||||
expect(component.popover()).toBeNull();
|
||||
expect(
|
||||
(fixture.nativeElement as HTMLElement).querySelector(
|
||||
'[role="tooltip"]'
|
||||
)
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('never shows a popover for a wide block', () => {
|
||||
const item = renderBlock({ tier: 'wide' });
|
||||
component.onBlockEnter(item, {
|
||||
currentTarget: {
|
||||
getBoundingClientRect: () => ({ left: 0, width: 200, bottom: 0 }),
|
||||
getBoundingClientRect: () => ({
|
||||
left: 0,
|
||||
width: 200,
|
||||
bottom: 0,
|
||||
}),
|
||||
},
|
||||
} as unknown as Event);
|
||||
expect(component.popover()).toBeNull();
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
computed,
|
||||
HostBinding,
|
||||
input,
|
||||
linkedSignal,
|
||||
output,
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { EpgProgram } from '@iptvnator/shared/interfaces';
|
||||
@@ -22,6 +22,8 @@ import {
|
||||
} from './epg-timeline.utils';
|
||||
|
||||
interface PopoverState {
|
||||
/** The render item it describes. */
|
||||
readonly key: string;
|
||||
readonly title: string;
|
||||
readonly desc: string | null;
|
||||
readonly startMs: number;
|
||||
@@ -60,7 +62,20 @@ export class EpgTimelineTrackComponent {
|
||||
readonly infoClick = output<TimelineBlock>();
|
||||
readonly groupExpand = output<TimelineRenderGroup>();
|
||||
|
||||
readonly popover = signal<PopoverState | null>(null);
|
||||
/**
|
||||
* Cleared when its block leaves the rendered items: the ribbon window can
|
||||
* remove a focused block on scroll, and a removed node fires no
|
||||
* `focusout`, so the tooltip would stay on a programme no longer shown.
|
||||
*/
|
||||
readonly popover = linkedSignal<TimelineRenderItem[], PopoverState | null>({
|
||||
source: this.items,
|
||||
computation: (items, previous) => {
|
||||
const shown = previous?.value;
|
||||
return shown && items.some((item) => item.key === shown.key)
|
||||
? shown
|
||||
: null;
|
||||
},
|
||||
});
|
||||
|
||||
@HostBinding('style.width.px') get hostWidth(): number {
|
||||
return this.trackWidthPx();
|
||||
@@ -116,6 +131,7 @@ export class EpgTimelineTrackComponent {
|
||||
const below = rect.bottom + 8;
|
||||
const flipAbove = below + estHeight > window.innerHeight;
|
||||
this.popover.set({
|
||||
key: item.key,
|
||||
title: item.block.program.title,
|
||||
desc: item.block.program.desc,
|
||||
startMs: item.block.startMs,
|
||||
@@ -128,9 +144,7 @@ export class EpgTimelineTrackComponent {
|
||||
rect.left + rect.width / 2 - width / 2
|
||||
)
|
||||
),
|
||||
top: flipAbove
|
||||
? Math.max(12, rect.top - estHeight - 8)
|
||||
: below,
|
||||
top: flipAbove ? Math.max(12, rect.top - estHeight - 8) : below,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user