ComponentProduct
Popover
Floating panel anchored to a trigger, for short forms and additional details.
$ npx context-ds add popoverUsage
Use it for
Small forms and details anchored to a control.
Avoid
- Essential content only in a popover
Dependencies: surface, @radix-ui/react-popover
Example
examples/popover.tsx
"use client";import { useState } from "react";import { Button } from "@/components/ui/button";import { Field } from "@/components/ui/field";import { Input } from "@/components/ui/input";import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";export function Product() { const [name, setName] = useState("Acme"); const [draft, setDraft] = useState(name); const [open, setOpen] = useState(false); return ( <div className="grid justify-items-start gap-3"> <p role="status" className="text-body-sm">Workspace: {name}</p> <Popover open={open} onOpenChange={setOpen}> <PopoverTrigger asChild><Button variant="outline" onClick={() => setDraft(name)}>Rename</Button></PopoverTrigger> <PopoverContent><form className="grid gap-3" onSubmit={(event) => { event.preventDefault(); if (!draft.trim()) return; setName(draft.trim()); setOpen(false); }}> <Field label="Workspace name" htmlFor="ws-name" required><Input id="ws-name" value={draft} onChange={(event) => setDraft(event.target.value)} /></Field> <Button type="submit" variant="primary" size="sm" disabled={!draft.trim()} className="justify-self-end">Save</Button> </form></PopoverContent> </Popover> </div> );}Source
components/ui/popover.tsx
"use client";import * as React from "react";import * as PopoverPrimitive from "@radix-ui/react-popover";import { usePortalContainer } from "@/lib/surface";import { cn } from "@/lib/utils";// The dashboard's floating panel: shell corners, a hairline in the shadow, 6px off its trigger, growing out of the// corner nearest the trigger.function Popover(props: React.ComponentProps<typeof PopoverPrimitive.Root>) { return <PopoverPrimitive.Root data-slot="popover" {...props} />;}function PopoverTrigger(props: React.ComponentProps<typeof PopoverPrimitive.Trigger>) { return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;}function PopoverContent({ className, align = "start", sideOffset = 6, ...props }: React.ComponentProps<typeof PopoverPrimitive.Content>) { return ( <PopoverPrimitive.Portal container={usePortalContainer()}> <PopoverPrimitive.Content data-slot="popover-content" align={align} sideOffset={sideOffset} collisionPadding={8} className={cn("ds-scrollbar z-50 max-h-(--radix-popover-content-available-height) w-72 max-w-[calc(100vw-1rem)] origin-(--radix-popover-content-transform-origin) animate-pop-in overflow-y-auto rounded-shell bg-surface p-3 text-body-sm text-fg shadow-floating outline-none", className)} {...props} /> </PopoverPrimitive.Portal> );}function PopoverAnchor(props: React.ComponentProps<typeof PopoverPrimitive.Anchor>) { return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />;}export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger };Reference: context-dashboard c0ddf7a src/components/ui/menu.tsx.