{
  "name": "popover",
  "type": "registry:component",
  "files": [
    {
      "name": "popover.component.ts",
      "content": "import { type ConnectedPosition, Overlay, OverlayPositionBuilder, type OverlayRef } from '@angular/cdk/overlay';\nimport { TemplatePortal } from '@angular/cdk/portal';\nimport { isPlatformBrowser } from '@angular/common';\nimport {\n  ChangeDetectionStrategy,\n  Component,\n  computed,\n  DestroyRef,\n  Directive,\n  ElementRef,\n  inject,\n  input,\n  type OnDestroy,\n  type OnInit,\n  output,\n  PLATFORM_ID,\n  Renderer2,\n  signal,\n  type TemplateRef,\n  ViewContainerRef,\n} from '@angular/core';\nimport { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';\n\nimport { filter, type Subscription } from 'rxjs';\n\nimport { mergeClasses } from '@/shared/utils/merge-classes';\n\nimport { popoverVariants } from './popover.variants';\n\nexport type ZardPopoverTrigger = 'click' | 'hover' | null;\nexport type ZardPopoverPlacement = 'top' | 'bottom' | 'left' | 'right';\n\nconst POPOVER_POSITIONS_MAP: { [key: string]: ConnectedPosition } = {\n  top: {\n    originX: 'center',\n    originY: 'top',\n    overlayX: 'center',\n    overlayY: 'bottom',\n    offsetX: 0,\n    offsetY: -8,\n  },\n  bottom: {\n    originX: 'center',\n    originY: 'bottom',\n    overlayX: 'center',\n    overlayY: 'top',\n    offsetX: 0,\n    offsetY: 8,\n  },\n  left: {\n    originX: 'start',\n    originY: 'center',\n    overlayX: 'end',\n    overlayY: 'center',\n    offsetX: -8,\n    offsetY: 0,\n  },\n  right: {\n    originX: 'end',\n    originY: 'center',\n    overlayX: 'start',\n    overlayY: 'center',\n    offsetX: 8,\n    offsetY: 0,\n  },\n} as const;\n\n@Directive({\n  selector: '[zPopover]',\n  standalone: true,\n  exportAs: 'zPopover',\n})\nexport class ZardPopoverDirective implements OnInit, OnDestroy {\n  private readonly destroyRef = inject(DestroyRef);\n  private readonly overlay = inject(Overlay);\n  private readonly overlayPositionBuilder = inject(OverlayPositionBuilder);\n  private readonly elementRef = inject(ElementRef);\n  private readonly renderer = inject(Renderer2);\n  private readonly viewContainerRef = inject(ViewContainerRef);\n  private readonly platformId = inject(PLATFORM_ID);\n\n  private overlayRef?: OverlayRef;\n  private overlayRefSubscription?: Subscription;\n  private listeners: (() => void)[] = [];\n\n  readonly zTrigger = input<ZardPopoverTrigger>('click');\n  readonly zContent = input.required<TemplateRef<unknown>>();\n  readonly zPlacement = input<ZardPopoverPlacement>('bottom');\n  readonly zOrigin = input<ElementRef>();\n  readonly zVisible = input<boolean>(false);\n  readonly zOverlayClickable = input<boolean>(true);\n  readonly zVisibleChange = output<boolean>();\n\n  private readonly isVisible = signal(false);\n\n  get nativeElement() {\n    return this.zOrigin()?.nativeElement ?? this.elementRef.nativeElement;\n  }\n\n  constructor() {\n    toObservable(this.zVisible)\n      .pipe(takeUntilDestroyed(this.destroyRef))\n      .subscribe(visible => {\n        const currentlyVisible = this.isVisible();\n        if (visible && !currentlyVisible) {\n          this.show();\n        } else if (!visible && currentlyVisible) {\n          this.hide();\n        }\n      });\n\n    toObservable(this.zTrigger)\n      .pipe(takeUntilDestroyed(this.destroyRef))\n      .subscribe(trigger => {\n        if (this.listeners.length) {\n          this.unlistenAll();\n        }\n        this.setupTriggers();\n        this.overlayRefSubscription?.unsubscribe();\n        this.overlayRefSubscription = undefined;\n        if (trigger === 'click') {\n          this.subscribeToOverlayRef();\n        }\n      });\n  }\n\n  ngOnInit() {\n    this.createOverlay();\n  }\n\n  ngOnDestroy() {\n    this.unlistenAll();\n    this.overlayRefSubscription?.unsubscribe();\n    this.overlayRef?.dispose();\n  }\n\n  show() {\n    if (this.isVisible()) {\n      return;\n    }\n\n    if (!this.overlayRef) {\n      this.createOverlay();\n    }\n\n    const templatePortal = new TemplatePortal(this.zContent(), this.viewContainerRef);\n    this.overlayRef?.attach(templatePortal);\n    this.isVisible.set(true);\n    this.zVisibleChange.emit(true);\n  }\n\n  hide() {\n    if (!this.isVisible()) {\n      return;\n    }\n\n    this.overlayRef?.detach();\n    this.isVisible.set(false);\n    this.zVisibleChange.emit(false);\n  }\n\n  toggle() {\n    if (this.isVisible()) {\n      this.hide();\n    } else {\n      this.show();\n    }\n  }\n\n  private createOverlay() {\n    if (isPlatformBrowser(this.platformId)) {\n      const positionStrategy = this.overlayPositionBuilder\n        .flexibleConnectedTo(this.nativeElement)\n        .withPositions(this.getPositions())\n        .withPush(false)\n        .withFlexibleDimensions(false)\n        .withViewportMargin(8);\n\n      this.overlayRef = this.overlay.create({\n        positionStrategy,\n        hasBackdrop: false,\n        scrollStrategy: this.overlay.scrollStrategies.reposition(),\n      });\n    }\n  }\n\n  private subscribeToOverlayRef(): void {\n    if (\n      this.zOverlayClickable() &&\n      this.zTrigger() === 'click' &&\n      isPlatformBrowser(this.platformId) &&\n      this.overlayRef\n    ) {\n      this.overlayRefSubscription = this.overlayRef\n        .outsidePointerEvents()\n        .pipe(filter(event => !this.nativeElement.contains(event.target)))\n        .subscribe(() => this.hide());\n    }\n  }\n\n  private setupTriggers() {\n    const trigger = this.zTrigger();\n    if (!trigger) {\n      return;\n    }\n\n    if (trigger === 'click') {\n      this.listeners.push(this.renderer.listen(this.nativeElement, 'click.stop', () => this.toggle()));\n    } else if (trigger === 'hover') {\n      this.listeners.push(this.renderer.listen(this.nativeElement, 'mouseenter', () => this.show()));\n\n      this.listeners.push(this.renderer.listen(this.nativeElement, 'mouseleave', () => this.hide()));\n    }\n  }\n\n  private unlistenAll(): void {\n    for (const listener of this.listeners) {\n      listener();\n    }\n    this.listeners = [];\n  }\n\n  private getPositions(): ConnectedPosition[] {\n    const placement = this.zPlacement();\n    const positions: ConnectedPosition[] = [];\n\n    // Primary position\n    const primaryConfig = POPOVER_POSITIONS_MAP[placement];\n    positions.push({\n      originX: primaryConfig.originX,\n      originY: primaryConfig.originY,\n      overlayX: primaryConfig.overlayX,\n      overlayY: primaryConfig.overlayY,\n      offsetX: primaryConfig.offsetX ?? 0,\n      offsetY: primaryConfig.offsetY ?? 0,\n    });\n\n    // Fallback positions for better positioning when primary doesn't fit\n    switch (placement) {\n      case 'bottom':\n        // Try top if bottom doesn't fit\n        positions.push({\n          originX: 'center',\n          originY: 'top',\n          overlayX: 'center',\n          overlayY: 'bottom',\n          offsetX: 0,\n          offsetY: -8,\n        });\n        // If neither top nor bottom work, try right\n        positions.push({\n          originX: 'end',\n          originY: 'center',\n          overlayX: 'start',\n          overlayY: 'center',\n          offsetX: 8,\n          offsetY: 0,\n        });\n        // Finally try left\n        positions.push({\n          originX: 'start',\n          originY: 'center',\n          overlayX: 'end',\n          overlayY: 'center',\n          offsetX: -8,\n          offsetY: 0,\n        });\n        break;\n      case 'top':\n        // Try bottom if top doesn't fit\n        positions.push({\n          originX: 'center',\n          originY: 'bottom',\n          overlayX: 'center',\n          overlayY: 'top',\n          offsetX: 0,\n          offsetY: 8,\n        });\n        // If neither top nor bottom work, try right\n        positions.push({\n          originX: 'end',\n          originY: 'center',\n          overlayX: 'start',\n          overlayY: 'center',\n          offsetX: 8,\n          offsetY: 0,\n        });\n        // Finally try left\n        positions.push({\n          originX: 'start',\n          originY: 'center',\n          overlayX: 'end',\n          overlayY: 'center',\n          offsetX: -8,\n          offsetY: 0,\n        });\n        break;\n      case 'right':\n        // Try left if right doesn't fit\n        positions.push({\n          originX: 'start',\n          originY: 'center',\n          overlayX: 'end',\n          overlayY: 'center',\n          offsetX: -8,\n          offsetY: 0,\n        });\n        // If neither left nor right work, try bottom\n        positions.push({\n          originX: 'center',\n          originY: 'bottom',\n          overlayX: 'center',\n          overlayY: 'top',\n          offsetX: 0,\n          offsetY: 8,\n        });\n        // Finally try top\n        positions.push({\n          originX: 'center',\n          originY: 'top',\n          overlayX: 'center',\n          overlayY: 'bottom',\n          offsetX: 0,\n          offsetY: -8,\n        });\n        break;\n      case 'left':\n        // Try right if left doesn't fit\n        positions.push({\n          originX: 'end',\n          originY: 'center',\n          overlayX: 'start',\n          overlayY: 'center',\n          offsetX: 8,\n          offsetY: 0,\n        });\n        // If neither left nor right work, try bottom\n        positions.push({\n          originX: 'center',\n          originY: 'bottom',\n          overlayX: 'center',\n          overlayY: 'top',\n          offsetX: 0,\n          offsetY: 8,\n        });\n        // Finally try top\n        positions.push({\n          originX: 'center',\n          originY: 'top',\n          overlayX: 'center',\n          overlayY: 'bottom',\n          offsetX: 0,\n          offsetY: -8,\n        });\n        break;\n    }\n\n    return positions;\n  }\n}\n\n@Component({\n  selector: 'z-popover',\n  imports: [],\n  standalone: true,\n  template: `\n    <ng-content />\n  `,\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  host: {\n    '[class]': 'classes()',\n  },\n})\nexport class ZardPopoverComponent {\n  readonly class = input<string>('');\n\n  protected readonly classes = computed(() => mergeClasses(popoverVariants(), this.class()));\n}\n"
    },
    {
      "name": "popover.variants.ts",
      "content": "import { cva, type VariantProps } from 'class-variance-authority';\n\nexport const popoverVariants = cva(\n  'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',\n);\n\nexport type ZardPopoverVariants = VariantProps<typeof popoverVariants>;\n"
    },
    {
      "name": "index.ts",
      "content": "export * from './popover.component';\nexport * from './popover.variants';\n"
    }
  ],
  "demos": [
    {
      "name": "default.ts",
      "content": "import { ChangeDetectionStrategy, Component } from '@angular/core';\n\nimport { ZardButtonComponent } from '../../button/button.component';\nimport { ZardPopoverComponent, ZardPopoverDirective } from '../popover.component';\n\n@Component({\n  selector: 'z-popover-default-demo',\n  imports: [ZardButtonComponent, ZardPopoverComponent, ZardPopoverDirective],\n  standalone: true,\n  template: `\n    <button type=\"button\" z-button zPopover [zContent]=\"popoverContent\" zType=\"outline\">Open popover</button>\n\n    <ng-template #popoverContent>\n      <z-popover>\n        <div class=\"space-y-2\">\n          <h4 class=\"leading-none font-medium\">Dimensions</h4>\n          <p class=\"text-muted-foreground text-sm\">Set the dimensions for the layer.</p>\n        </div>\n      </z-popover>\n    </ng-template>\n  `,\n  changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ZardDemoPopoverDefaultComponent {}\n"
    },
    {
      "name": "hover.ts",
      "content": "import { ChangeDetectionStrategy, Component } from '@angular/core';\n\nimport { ZardButtonComponent } from '../../button/button.component';\nimport { ZardPopoverComponent, ZardPopoverDirective } from '../popover.component';\n\n@Component({\n  selector: 'z-popover-hover-demo',\n  imports: [ZardButtonComponent, ZardPopoverComponent, ZardPopoverDirective],\n  standalone: true,\n  template: `\n    <button z-button zPopover [zContent]=\"popoverContent\" zTrigger=\"hover\" zType=\"outline\">Hover me</button>\n\n    <ng-template #popoverContent>\n      <z-popover>\n        <div class=\"space-y-2\">\n          <h4 class=\"leading-none font-medium\">Hover content</h4>\n          <p class=\"text-muted-foreground text-sm\">This popover appears when you hover over the button.</p>\n        </div>\n      </z-popover>\n    </ng-template>\n  `,\n  changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ZardDemoPopoverHoverComponent {}\n"
    },
    {
      "name": "interactive.ts",
      "content": "import { ChangeDetectionStrategy, Component, signal, viewChild } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\n\nimport { ZardButtonComponent } from '../../button/button.component';\nimport { ZardInputComponent } from '../../input/input.component';\nimport { ZardPopoverComponent, ZardPopoverDirective } from '../popover.component';\n\n@Component({\n  selector: 'z-popover-interactive-demo',\n  imports: [FormsModule, ZardButtonComponent, ZardPopoverComponent, ZardPopoverDirective, ZardInputComponent],\n  standalone: true,\n  template: `\n    <button z-button zPopover [zContent]=\"interactiveContent\" zType=\"outline\" #popoverTrigger>Settings</button>\n\n    <ng-template #interactiveContent>\n      <z-popover>\n        <div class=\"space-y-4\">\n          <div class=\"space-y-2\">\n            <h4 class=\"leading-none font-medium\">Settings</h4>\n            <p class=\"text-muted-foreground text-sm\">Manage your account settings.</p>\n          </div>\n          <div class=\"space-y-2\">\n            <label for=\"width\" class=\"text-sm font-medium\">Width</label>\n            <input id=\"width\" z-input type=\"text\" placeholder=\"100%\" class=\"w-full\" [(ngModel)]=\"width\" />\n          </div>\n          <div class=\"space-y-2\">\n            <label for=\"height\" class=\"text-sm font-medium\">Height</label>\n            <input id=\"height\" z-input type=\"text\" placeholder=\"25px\" class=\"w-full\" [(ngModel)]=\"height\" />\n          </div>\n          <button z-button class=\"w-full\" zSize=\"sm\" (click)=\"saveChanges()\">Save changes</button>\n        </div>\n      </z-popover>\n    </ng-template>\n  `,\n  changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ZardDemoPopoverInteractiveComponent {\n  readonly popoverDirective = viewChild.required('popoverTrigger', { read: ZardPopoverDirective });\n\n  readonly width = signal('100%');\n  readonly height = signal('25px');\n\n  saveChanges() {\n    console.log('Settings saved:', { width: this.width(), height: this.height() });\n    this.popoverDirective().hide();\n  }\n}\n"
    },
    {
      "name": "placement.ts",
      "content": "import { ChangeDetectionStrategy, Component } from '@angular/core';\n\nimport { ZardButtonComponent } from '../../button/button.component';\nimport { ZardPopoverComponent, ZardPopoverDirective } from '../popover.component';\n\n@Component({\n  selector: 'z-popover-placement-demo',\n  imports: [ZardButtonComponent, ZardPopoverComponent, ZardPopoverDirective],\n  standalone: true,\n  template: `\n    <div class=\"flex flex-col space-y-2\">\n      <button z-button zPopover [zContent]=\"popoverContent\" zPlacement=\"top\" zType=\"outline\">Top</button>\n\n      <div class=\"flex space-x-2\">\n        <button z-button zPopover [zContent]=\"popoverContent\" zPlacement=\"left\" zType=\"outline\">Left</button>\n        <button z-button zPopover [zContent]=\"popoverContent\" zPlacement=\"right\" zType=\"outline\">Right</button>\n      </div>\n\n      <button z-button zPopover [zContent]=\"popoverContent\" zPlacement=\"bottom\" zType=\"outline\">Bottom</button>\n    </div>\n\n    <ng-template #popoverContent>\n      <z-popover>\n        <div class=\"space-y-2\">\n          <h4 class=\"leading-none font-medium\">Popover content</h4>\n          <p class=\"text-muted-foreground text-sm\">This is the popover content.</p>\n        </div>\n      </z-popover>\n    </ng-template>\n  `,\n  changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ZardDemoPopoverPlacementComponent {}\n"
    }
  ]
}
