Popover
@xui/popoverA floating panel anchored to its trigger. The base of every Phase 3 overlay preset: tooltip is a hover popover, menu a click popover holding a menu. Positioning, Escape, outside-click and focus all come from @xui/core/overlay, so they cannot be checked in jsdom — these stories are where that behaviour is verified.
Install
pnpm add @xui/popover Add the barrel to a standalone component's imports:
import { XuiPopoverImports } from '@xui/popover';
@Component({
imports: [XuiPopoverImports],
// …
})Examples
Click
Loading preview…
<button xuiButton [xuiPopover]="content">Open popover</button>
<ng-template #content>
<div class="max-w-xs p-4">
<p class="text-foreground font-semibold">Confirm deploy</p>
<p class="text-foreground-muted mt-1 text-sm">Ship the current build to production?</p>
<div class="mt-3 flex justify-end gap-2">
<button xuiButton size="sm" variant="ghost">Cancel</button>
<button xuiButton size="sm">Deploy</button>
</div>
</div>
</ng-template>Hover
Loading preview…
<a class="text-link cursor-pointer underline" tabindex="0" [xuiPopover]="card" interactionKind="hover">@rikarin</a>
<ng-template #card>
<div class="w-64 p-4">
<p class="text-foreground font-semibold">Jiu</p>
<p class="text-foreground-muted mt-1 text-sm">Maintainer of xUI. Hover cards never steal focus.</p>
</div>
</ng-template>Placements
Loading preview…
<div class="flex flex-wrap gap-2">
@for (p of placements; track p) {
<button xuiButton size="sm" variant="outline" (click)="placement.set(p)">{{ p }}</button>
}
</div>
<div class="mt-24 flex justify-center">
<button xuiButton [xuiPopover]="tip" [placement]="placement()" interactionKind="hover">
{{ placement() }}
</button>
</div>
<ng-template #tip><div class="px-3 py-2 text-sm">Placed {{ placement() }}</div></ng-template>Match width
Loading preview…
<button xuiButton class="w-72 justify-between" [xuiPopover]="opts" matchTargetWidth minimal>
Pick an environment
</button>
<ng-template #opts>
<div class="flex flex-col py-1">
@for (env of envs; track env) {
<button class="hover:bg-hover-overlay px-3 py-1.5 text-left text-sm">{{ env }}</button>
}
</div>
</ng-template>API
The floating surface a popover's content sits on.
Anchors a floating panel to the element it sits on.
Inputs
contentTemplateRef<unknown>The content to float. Aliased so the selector doubles as the content input.contextRecord<string, unknown>Values the content template destructures with let-.interactionKindXuiPopoverInteractionKindWhat opens the popover: a click or a hover. The -target variants keep the popover open only while the target itself is engaged, rather than bridging to the panel.this.config.interactionKindplacementXPlacementPreferred side and alignment relative to the target. The overlay flips it when there is no room.this.config.placementoffsetnumberGap in pixels between the target and the panel. Ignored when minimal, which sits flush.this.config.offsethoverOpenDelaynumberMilliseconds the pointer must rest on the target before a hover popover opens. Keeps it from firing on the way past.this.config.hoverOpenDelayhoverCloseDelaynumberMilliseconds before a hover popover closes after the pointer leaves — the grace period for crossing the gap into the panel.this.config.hoverCloseDelayminimalbooleanDrop the arrow and the offset, so the panel sits flush against the target. For dropdowns rather than callouts.this.config.minimalmatchTargetWidthbooleanMake the panel exactly as wide as its target. What a select-style dropdown wants.this.config.matchTargetWidthbarebooleanThe content already is a surface, so the panel should only position it. For a picker card, which brings its own background, border and rounding — without this the panel draws a second surface underneath, and its width cap crops the card.falsedisabledbooleanA disabled trigger never opens, and closes the popover if it was open.falsepanelClassClassValueExtra classes for the floating surface.''role'dialog' | 'menu' | 'listbox' | 'grid' | nullARIA role for the pane — menu, listbox, dialog. Null leaves it a plain group.nullariaLabelstring | nullAccessible name for the panel, when its content does not supply one.nullopenbooleanOpen state. Works bound (controlled) or unbound (the directive toggles it).falseOutputs
openedvoidEmits once the panel is attached.closedvoidEmits once the panel is detached.Methods
toggle(): voidToggle from a template, e.g. a dedicated open button.show(): voidclose(): void