import { Component, ViewChild, ElementRef, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; import { PopoverComponent } from '../../../../../ui-essentials/src/lib/components/overlays/popover'; type PopoverPosition = 'top' | 'bottom' | 'left' | 'right' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end'; type PopoverSize = 'sm' | 'md' | 'lg'; type PopoverVariant = 'default' | 'elevated' | 'floating' | 'menu' | 'tooltip'; type PopoverTrigger = 'click' | 'hover' | 'focus' | 'manual'; @Component({ selector: 'ui-popover-demo', standalone: true, imports: [CommonModule, PopoverComponent], template: `

Popover Component

A flexible popover component with multiple positioning options, trigger types, and variants. Perfect for dropdowns, tooltips, context menus, and floating content.

Sizes

@for (size of sizes; track size) {
This is a {{ size }} sized popover with sample content to demonstrate the different size options available.
}

Variants

@for (variant of variants; track variant) {
@if (variant === 'tooltip') {
Quick tooltip content
} @else {
{{ variant | titlecase }} Variant

This demonstrates the {{ variant }} styling variant of the popover component.

}
}

Positions

Reference Element
@for (position of positions; track position.key) {
{{ position.value | titlecase }}

Positioned {{ position.value }} relative to trigger.

}

Trigger Types

This popover appears on hover with a 500ms delay.

This popover appears when the input gains focus and helps guide the user.

Advanced Examples

JD
Full-stack developer with 5+ years of experience in Angular, Node.js, and cloud technologies. Passionate about creating user-friendly interfaces and scalable applications.

Configuration Options

Auto-positioning Enabled

This popover automatically adjusts its position to stay within the viewport.

Current Position: {{ currentAutoPosition() || 'right' }}

Modal-like Popover

This popover has a backdrop and prevents body scroll. Click the backdrop or press ESC to close.

Total Interactions: {{ totalInteractions() }} Backdrop Clicks: {{ backdropClicks() }} Auto-repositions: {{ autoRepositions() }}
<!-- Basic Usage -->
<button #trigger>Click me</button>
<ui-popover [triggerElement]="trigger" position="bottom" trigger="click">
  <p>Popover content goes here</p>
</ui-popover>

<!-- Advanced Configuration -->
<ui-popover
  [triggerElement]="myTrigger"
  position="bottom-start"
  variant="menu"
  trigger="click"
  [autoPosition]="true"
  [showArrow]="true"
  (visibleChange)="onPopoverToggle($event)">
  <div class="custom-menu">...</div>
</ui-popover>
`, styleUrl: './popover-demo.component.scss' }) export class PopoverDemoComponent { // Component references @ViewChild('triggerTopStart', { read: ElementRef }) triggerTopStart?: ElementRef; @ViewChild('triggerTop', { read: ElementRef }) triggerTop?: ElementRef; @ViewChild('triggerTopEnd', { read: ElementRef }) triggerTopEnd?: ElementRef; @ViewChild('triggerLeft', { read: ElementRef }) triggerLeft?: ElementRef; @ViewChild('triggerRight', { read: ElementRef }) triggerRight?: ElementRef; @ViewChild('triggerBottomStart', { read: ElementRef }) triggerBottomStart?: ElementRef; @ViewChild('triggerBottom', { read: ElementRef }) triggerBottom?: ElementRef; @ViewChild('triggerBottomEnd', { read: ElementRef }) triggerBottomEnd?: ElementRef; // Demo data readonly sizes: PopoverSize[] = ['sm', 'md', 'lg']; readonly variants: PopoverVariant[] = ['default', 'elevated', 'floating', 'menu', 'tooltip']; readonly triggers: PopoverTrigger[] = ['click', 'hover', 'focus', 'manual']; readonly positions = [ { key: 'top-start', value: 'top-start' as PopoverPosition }, { key: 'top', value: 'top' as PopoverPosition }, { key: 'top-end', value: 'top-end' as PopoverPosition }, { key: 'left', value: 'left' as PopoverPosition }, { key: 'right', value: 'right' as PopoverPosition }, { key: 'bottom-start', value: 'bottom-start' as PopoverPosition }, { key: 'bottom', value: 'bottom' as PopoverPosition }, { key: 'bottom-end', value: 'bottom-end' as PopoverPosition } ]; // State management private popoverStates = new Map(); // Statistics private _totalInteractions = signal(0); private _backdropClicks = signal(0); private _autoRepositions = signal(0); private _currentAutoPosition = signal(null); readonly totalInteractions = this._totalInteractions.asReadonly(); readonly backdropClicks = this._backdropClicks.asReadonly(); readonly autoRepositions = this._autoRepositions.asReadonly(); readonly currentAutoPosition = this._currentAutoPosition.asReadonly(); /** * Gets the state of a popover by key */ getPopoverState(key: string): boolean { return this.popoverStates.get(key) || false; } /** * Sets the state of a popover by key */ setPopoverState(key: string, visible: boolean): void { this.popoverStates.set(key, visible); if (visible) { this._totalInteractions.update(count => count + 1); } } /** * Toggles a popover state */ togglePopover(key: string): void { const currentState = this.getPopoverState(key); this.setPopoverState(key, !currentState); } /** * Gets the trigger element for position demos */ getPositionTrigger(position: string): HTMLElement | undefined { switch (position) { case 'top-start': return this.triggerTopStart?.nativeElement; case 'top': return this.triggerTop?.nativeElement; case 'top-end': return this.triggerTopEnd?.nativeElement; case 'left': return this.triggerLeft?.nativeElement; case 'right': return this.triggerRight?.nativeElement; case 'bottom-start': return this.triggerBottomStart?.nativeElement; case 'bottom': return this.triggerBottom?.nativeElement; case 'bottom-end': return this.triggerBottomEnd?.nativeElement; default: return undefined; } } /** * Handles menu actions */ handleMenuAction(action: string): void { console.log('Menu action:', action); this.setPopoverState('menu-dropdown', false); // You could implement actual menu logic here } /** * Handles form submission */ handleFormSubmit(): void { console.log('Form submitted'); this.setPopoverState('form-popover', false); // You could implement actual form logic here } /** * Handles user profile actions */ handleUserAction(action: string): void { console.log('User action:', action); this.setPopoverState('user-card', false); // You could implement actual user interaction logic here } /** * Handles position changes from auto-positioning */ handlePositionChange(newPosition: PopoverPosition): void { console.log('Position changed to:', newPosition); this._currentAutoPosition.set(newPosition); this._autoRepositions.update(count => count + 1); } /** * Handles backdrop clicks */ handleBackdropClick(): void { console.log('Backdrop clicked'); this._backdropClicks.update(count => count + 1); } }