---
/**
* Popover — a popover anchored to its trigger, zero JS.
*
* The popover attribute gives open/close, light dismiss, Escape and the top
* layer. Anchor positioning places it beside the trigger and flips it when
* there is no room; @starting-style animates it in and allow-discrete out.
* Browsers without anchor positioning show it centred at the bottom of the
* viewport, still fully functional.
*/
import type { HTMLAttributes } from 'astro/types';
interface Props extends HTMLAttributes<'div'> {
/** Popover id; the trigger points at it. */
id: string;
/** Trigger label; or use the `trigger` slot. */
label?: string;
/** position-area value: top, bottom, left, right, or any two-keyword form. */
placement?: string;
/** Gap from the trigger, any CSS length. */
gap?: string;
}
const { id, label = 'Open', placement = 'bottom', gap = '0.5rem', class: className, style, ...rest } = Astro.props;
const anchor = `--ma-pop-${id}`;
const inline = [`position-anchor:${anchor}`, `--ma-pop-area:${placement}`, `--ma-pop-gap:${gap}`, typeof style === 'string' ? style : ''].filter(Boolean).join(';');
const triggerAttrs = { popovertarget: id, style: `anchor-name:${anchor}` };
const popAttrs = { popover: 'auto' };
---
{Astro.slots.has('trigger') ? <slot name="trigger" /> : <button type="button" class="ma-popover__trigger" {...triggerAttrs}>{label}</button>}
<div id={id} class:list={['ma-popover', className]} style={inline} {...popAttrs} {...rest}>
<slot />
</div>
<style is:global>
@layer components {
:where(.ma-popover) {
position: fixed;
inset: auto;
position-area: var(--ma-pop-area, bottom);
position-try-fallbacks: flip-block, flip-inline;
margin: var(--ma-pop-gap, 0.5rem);
/* two hooks for a component that builds on Popover (MegaMenu): its own width and padding reach the popover even
where this rule comes later in the page's CSS than its own */
max-width: var(--ma-pop-max, min(20rem, calc(100vw - 2rem)));
border: 1px solid var(--ma-edge);
border-radius: 0.75rem;
padding: var(--ma-pop-pad, 0.875rem 1rem);
background: var(--ma-panel);
color: inherit;
box-shadow: 0 12px 32px -16px rgb(0 0 0 / 0.35);
opacity: 0;
scale: 0.96;
transition:
opacity var(--ma-duration-fast) var(--ma-ease-out),
scale var(--ma-duration-fast) var(--ma-ease-out),
display var(--ma-duration-fast) allow-discrete,
overlay var(--ma-duration-fast) allow-discrete;
}
:where(.ma-popover:popover-open) {
opacity: 1;
scale: 1;
transition-duration: var(--ma-duration);
transition-timing-function: var(--ma-ease);
}
@starting-style {
:where(.ma-popover:popover-open) {
opacity: 0;
scale: 0.96;
}
}
:where(.ma-popover)::backdrop {
background: transparent;
}
:where(.ma-popover__trigger) {
cursor: pointer;
}
/* no anchor positioning: a small sheet at the bottom of the viewport */
@supports not (position-area: bottom) {
:where(.ma-popover) {
inset: auto 0 1.5rem;
margin-inline: auto;
}
}
@media (prefers-reduced-motion: reduce) {
:where(.ma-popover),
:where(.ma-popover:popover-open) {
scale: 1;
transition-duration: var(--ma-duration-fast);
transition-timing-function: linear;
}
}
}
</style>