Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion libs/components/src/popover/popover-anchor-for.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ export class PopoverAnchorForDirective implements OnDestroy {
/** Padding around the spotlight cutout in pixels */
readonly spotlightPadding = input<number>(0);

/**
* CSS selector to find a child element within the anchor to use as the spotlight target.
* If not provided, the anchor element itself is used.
*/
readonly spotlightTargetSelector = input<string>();

private overlayRef: OverlayRef | null = null;
private closedEventsSub: Subscription | null = null;
private readonly hasInitialized = signal(false);
Expand Down Expand Up @@ -144,8 +150,13 @@ export class PopoverAnchorForDirective implements OnDestroy {

// Create the spotlight border overlay first so the popover overlay sits above it in DOM order
if (this.spotlight()) {
const selector = this.spotlightTargetSelector();
const spotlightEl = selector
? ((this.elementRef.nativeElement.querySelector(selector) as HTMLElement | null) ??
this.elementRef.nativeElement)
: this.elementRef.nativeElement;
this.spotlightService.register(this);
this.spotlightService.showSpotlight(this.elementRef.nativeElement, this.spotlightPadding());
this.spotlightService.showSpotlight(spotlightEl, this.spotlightPadding());
}

this.popoverOpen.set(true);
Expand Down
11 changes: 11 additions & 0 deletions libs/components/src/popover/spotlight.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class SpotlightService {
private currentPadding = 0;
private borderOverlayRef: OverlayRef | null = null;
private resizeObserver: ResizeObserver | null = null;
private windowResizeListener: (() => void) | null = null;
private hideTimeout: number | null = null;
private activePopover: PopoverAnchorForDirective | null = null;

Expand Down Expand Up @@ -172,11 +173,21 @@ export class SpotlightService {
this.borderOverlayRef.updatePosition();
});
this.resizeObserver.observe(target);

// Reposition on window resize β€” CDK's reposition scroll strategy only covers scroll events
this.windowResizeListener = () => {
this.borderOverlayRef?.updatePosition();
};
window.addEventListener("resize", this.windowResizeListener);
Comment thread
BryanCunningham marked this conversation as resolved.
}

private disposeBorderOverlay(): void {
this.resizeObserver?.disconnect();
this.resizeObserver = null;
if (this.windowResizeListener) {
window.removeEventListener("resize", this.windowResizeListener);
this.windowResizeListener = null;
}
this.borderOverlayRef?.dispose(); // CDK moves borderElement back to document.body
this.borderOverlayRef = null;
this.borderElement.style.display = "none";
Expand Down
Loading