Popover
Hoja clienteA non-modal floating panel anchored to a trigger.
Vista previa en vivo
Renderizado desde el código fuente instalado de VEXA — los mismos archivos que recibes.
Instalación
Añade el código del componente a tu proyecto. Las dependencias del registry se resuelven automáticamente.
npx shadcn@latest add https://vexa.valfiguer.com/r/popover.jsonSe instala en components/vexa/popover.tsx
Dependencias npm
- @radix-ui/react-popover
Dependencias del registry
- @vexa/utils
Otros items de VEXA que necesita este componente. Instálalos primero — la CLI los resuelve automáticamente.
Código
Cópialo a su ruta destino, o instálalo con el comando de arriba.
"use client";
import type * as React from "react";
import * as PopoverPrimitive from "@radix-ui/react-popover";
import { cn } from "@/lib/utils";
/**
* Popover — a non-modal floating panel anchored to a trigger.
*
* Client leaf. Radix manages positioning, outside-click and Escape dismissal,
* and returns focus to the trigger on close. Unlike a Dialog it does not trap
* focus or make the page inert — use it for rich, non-critical content
* (filters, pickers, help). Give the content an accessible name when it has no
* heading.
*/
export function Popover(
props: React.ComponentProps<typeof PopoverPrimitive.Root>,
) {
return <PopoverPrimitive.Root data-slot="popover" {...props} />;
}
export function PopoverTrigger(
props: React.ComponentProps<typeof PopoverPrimitive.Trigger>,
) {
return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
}
export function PopoverAnchor(
props: React.ComponentProps<typeof PopoverPrimitive.Anchor>,
) {
return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />;
}
export function PopoverContent({
className,
align = "center",
sideOffset = 6,
...props
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
return (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
data-slot="popover-content"
align={align}
sideOffset={sideOffset}
className={cn(
"z-50 w-72 rounded-vexa-md border border-vexa-border bg-vexa-surface-raised p-4 text-vexa-foreground shadow-vexa-lg outline-none",
"transition-[opacity,transform] duration-[var(--vexa-duration-fast)] ease-[var(--vexa-ease-standard)] motion-reduce:transition-none",
"data-[state=closed]:scale-95 data-[state=closed]:opacity-0 data-[state=open]:scale-100 data-[state=open]:opacity-100",
className,
)}
{...props}
/>
</PopoverPrimitive.Portal>
);
}
JSON del registryr/popover.json
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "popover",
"type": "registry:ui",
"title": "Popover",
"description": "A non-modal floating panel anchored to a trigger.",
"dependencies": [
"@radix-ui/react-popover"
],
"registryDependencies": [
"@vexa/utils"
],
"files": [
{
"path": "registry/default/ui/popover.tsx",
"type": "registry:ui",
"target": "components/vexa/popover.tsx",
"content": "\"use client\";\n\nimport type * as React from \"react\";\nimport * as PopoverPrimitive from \"@radix-ui/react-popover\";\n\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Popover — a non-modal floating panel anchored to a trigger.\n *\n * Client leaf. Radix manages positioning, outside-click and Escape dismissal,\n * and returns focus to the trigger on close. Unlike a Dialog it does not trap\n * focus or make the page inert — use it for rich, non-critical content\n * (filters, pickers, help). Give the content an accessible name when it has no\n * heading.\n */\nexport function Popover(\n props: React.ComponentProps<typeof PopoverPrimitive.Root>,\n) {\n return <PopoverPrimitive.Root data-slot=\"popover\" {...props} />;\n}\n\nexport function PopoverTrigger(\n props: React.ComponentProps<typeof PopoverPrimitive.Trigger>,\n) {\n return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />;\n}\n\nexport function PopoverAnchor(\n props: React.ComponentProps<typeof PopoverPrimitive.Anchor>,\n) {\n return <PopoverPrimitive.Anchor data-slot=\"popover-anchor\" {...props} />;\n}\n\nexport function PopoverContent({\n className,\n align = \"center\",\n sideOffset = 6,\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Content>) {\n return (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n data-slot=\"popover-content\"\n align={align}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 w-72 rounded-vexa-md border border-vexa-border bg-vexa-surface-raised p-4 text-vexa-foreground shadow-vexa-lg outline-none\",\n \"transition-[opacity,transform] duration-[var(--vexa-duration-fast)] ease-[var(--vexa-ease-standard)] motion-reduce:transition-none\",\n \"data-[state=closed]:scale-95 data-[state=closed]:opacity-0 data-[state=open]:scale-100 data-[state=open]:opacity-100\",\n className,\n )}\n {...props}\n />\n </PopoverPrimitive.Portal>\n );\n}\n"
}
],
"meta": {
"frameworks": [
"react",
"next"
],
"rsc": "client",
"exports": [
"Popover",
"PopoverTrigger",
"PopoverAnchor",
"PopoverContent"
],
"cssVars": [
"--vexa-border",
"--vexa-surface-raised",
"--vexa-foreground"
],
"a11y": "Non-modal: outside-click and Escape dismiss it and focus returns to the trigger; it does not trap focus or make the page inert. Give the content an accessible name when it has no heading.",
"example": "<Popover><PopoverTrigger asChild><Button variant=\"outline\">Filters</Button></PopoverTrigger><PopoverContent>...</PopoverContent></Popover>",
"tests": "planned — see docs/README.md#roadmap"
}
}Detalles
Accesibilidad
Non-modal: outside-click and Escape dismiss it and focus returns to the trigger; it does not trap focus or make the page inert. Give the content an accessible name when it has no heading.
Uso
A non-modal floating panel anchored to a trigger — extra detail, a small form, or filters — while the page stays interactive.
Anatomía
- Popover
- Root; owns the open state.
- PopoverTrigger
- The control that opens the panel.
- PopoverAnchor
- Optional element to position against.
- PopoverContent
- The floating panel.
Accesibilidad
Non-modal content. Give the content an accessible name (`aria-label`) when it has no visible heading.
Teclado
| Tecla | Acción |
|---|---|
| Escape | Close the popover |
| Tab | Move through the content and out (focus is not trapped) |
Foco: Outside-click and Escape dismiss it and return focus to the trigger; the page is not made inert.
- Not for critical modal flows — it neither traps focus nor blocks the page.
Buenas prácticas
Recomendado
- Give the content an accessible name.
- Use it for non-critical, dismissible content.
- Return focus to the trigger on close.
Evitar
- Don't use it for content that must be modal — use Dialog.
- Don't assume focus is trapped.
- Don't stack popovers on top of each other.
¿Te ha resultado útil esta página?
Guardado localmente en este navegador. Sin cuenta ni servidor.