Atomic Angular — Doc interne

Popover

Enhance the native HTML Popover API with Floating UI positioning — the bare primitive Menu and every other floating panel in this library are built from.

PopoverDirective wraps the native HTML Popover API (the popover attribute) with intelligent positioning from Floating UI. It responds to two selectors — [popover] and [dropdown] — which are the same directive; [dropdown] only changes one default. Reach for it directly when the floating content is not a list of actions; when it is, Menu is built on exactly this directive and adds the ARIA menu semantics on top.

Concept — the native Popover API

An element with a popover attribute renders in the browser's top layer — above everything else, no z-index required — and is shown/hidden by the browser itself: native focus handling, Escape-to-close and click-outside dismissal all come from the platform. PopoverDirective adds what the platform does not: computed placement relative to a trigger, collision-aware repositioning, and a couple of small ergonomics the raw attribute leaves to you.

Minimal example

A basic popover
sample.component.ts
import { Component } from "@angular/core";
import { DropdownDirective } from "@sinequa/galactik";

@Component({
  selector: "sample-component",
  imports: [DropdownDirective],
  template: `
    <button popovertarget="my-popover">Open popover</button>

    <div popover id="my-popover" class="w-64 p-3">
      <p>This is popover content.</p>
    </div>
  `,
})
export class SampleComponent {}

The trigger/target contract is entirely native HTML: any <button> with popovertarget="<id>" auto-invokes the element carrying [popover]/[dropdown] with a matching, page-unique id — PopoverDirective only adds the positioning once it opens.

How it works

native click toggle event state.set, open computePosition placement+offset+flip+shift left/top styles click closeOnClick / scroll closeOnScroll hidePopover button popovertarget native Popover API PopoverDirective autoUpdate floating-ui/dom div popover / dropdown

The element starts visibility: hidden until the first position is computed, avoiding a flash at the wrong spot. autoUpdate (repositioning on scroll, resize, or a DOM mutation near the trigger) only runs while the popover is open — zero overhead while closed. [dropdown] is the exact same directive as [popover]; it only changes what closeOnClick defaults to (see Options).

Recipes

Use [dropdown] for menus or lists where picking an item should always close the panel. PopoverDirective is a bare positioning primitive with no "menu item" look of its own — factor the item classes into your own reusable component, or reach for Menu when the content is an actions list with keyboard navigation.

Actions panel that closes on click
dropdown.component.ts
import { Component } from "@angular/core";
import { DropdownDirective } from "@sinequa/galactik";

@Component({
  selector: "sample-component",
  imports: [DropdownDirective],
  template: `
    <button popovertarget="my-dropdown">Actions</button>

    <div dropdown id="my-dropdown" placement="bottom-start" class="min-w-48">
      <button class="flex w-full items-center rounded-xs px-3 py-2 text-left text-sm hover:bg-(--bg-primary-lighter)">Edit</button>
      <button class="flex w-full items-center rounded-xs px-3 py-2 text-left text-sm hover:bg-(--bg-primary-lighter)">Duplicate</button>
    </div>
  `,
})
export class SampleComponent {}

Opt a [dropdown] out of its default click-to-close with an explicit [config]: [config]="{ closeOnClick: false }".

Locking the position, for a trigger that moves

By default flip()/shift() reposition the popover if the viewport runs out of room. [lockPosition]="true" disables both, forcing it to stay at the exact configured placement — useful when a consuming component already accounts for available space itself and a repositioning popover would fight that logic.

A popover pinned to one side
sample.component.ts
import { Component } from "@angular/core";
import { DropdownDirective } from "@sinequa/galactik";

@Component({
  selector: "sample-component",
  imports: [DropdownDirective],
  template: `
    <button popovertarget="pinned">More filters</button>

    <div popover id="pinned" placement="bottom-start" [lockPosition]="true" class="w-72 p-3">
      <p>Always opens bottom-start, regardless of available viewport space.</p>
    </div>
  `,
})
export class SampleComponent {}

Hiding the popover when its trigger scrolls away

A popover lives in the browser's top layer, so no ancestor clips it: when its trigger scrolls out of the list or container it sits in, the popover keeps floating over nothing. [hideWhenTriggerHidden]="true" hides it (visibility: hidden, it stays open) while the trigger is out of sight, and shows it again when the trigger comes back.

A popover that follows its trigger out of sight
sample.component.ts
import { Component } from "@angular/core";
import { DropdownDirective } from "@sinequa/galactik";

@Component({
  selector: "sample-component",
  imports: [DropdownDirective],
  template: `
    <div class="h-48 overflow-auto">
      <button popovertarget="row-actions">Row actions</button>
      <div class="h-96"></div>
    </div>

    <div popover id="row-actions" [hideWhenTriggerHidden]="true" class="w-56 p-3">
      <p>Hidden while the button is scrolled out of the box.</p>
    </div>
  `,
})
export class SampleComponent {}

Programmatic control and reactive state

Grab the directive instance with exportAs="popover" (or "dropdown") to drive it imperatively, and read state() to react to it.

Controlling and observing a popover
sample.component.ts
import { Component } from "@angular/core";
import { DropdownDirective } from "@sinequa/galactik";

@Component({
  selector: "sample-component",
  imports: [DropdownDirective],
  template: `
    <button popovertarget="state-demo" #ref="popover">Toggle</button>

    <div popover id="state-demo" #statePopover="popover">
      <p>Popover content</p>
      <button (click)="statePopover.close()">Close</button>
    </div>

    @if (statePopover.state() === 'open') {
      <p>Popover is currently open!</p>
    }
  `,
})
export class SampleComponent {}

Options

Prop

Type

Read through a template reference (#ref="popover" or "dropdown"): width (Signal<number>, the trigger's offsetWidth, handy for width-matched panels) and state (Signal<"open" | "closed">). Methods: close(), show(), toggle(). No outputs — react to state() instead.

Pitfalls

What's next

On this page