Saltar al contenido
VEXA
Overlays

Popover

Hoja cliente

A 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.

Escritorio

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.json

Se 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.

components/vexa/popover.tsx
"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
r/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.