Fortura/apps/www/public/r/radix-hover-card.json
2025-08-20 04:12:49 -06:00

19 lines
4.2 KiB
JSON

{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "radix-hover-card",
"type": "registry:ui",
"title": "Hover Card",
"description": "For sighted users to preview content available behind a link.",
"dependencies": [
"motion",
"radix-ui"
],
"files": [
{
"path": "registry/radix/hover-card/index.tsx",
"content": "'use client';\n\nimport * as React from 'react';\nimport { HoverCard as HoverCardPrimitive } from 'radix-ui';\nimport {\n AnimatePresence,\n motion,\n type HTMLMotionProps,\n type Transition,\n} from 'motion/react';\n\nimport { cn } from '@/lib/utils';\n\ntype HoverCardContextType = {\n isOpen: boolean;\n};\n\nconst HoverCardContext = React.createContext<HoverCardContextType | undefined>(\n undefined,\n);\n\nconst useHoverCard = (): HoverCardContextType => {\n const context = React.useContext(HoverCardContext);\n if (!context) {\n throw new Error('useHoverCard must be used within a HoverCard');\n }\n return context;\n};\n\ntype Side = 'top' | 'bottom' | 'left' | 'right';\n\nconst getInitialPosition = (side: Side) => {\n switch (side) {\n case 'top':\n return { y: 15 };\n case 'bottom':\n return { y: -15 };\n case 'left':\n return { x: 15 };\n case 'right':\n return { x: -15 };\n }\n};\n\ntype HoverCardProps = React.ComponentProps<typeof HoverCardPrimitive.Root>;\n\nfunction HoverCard({ children, ...props }: HoverCardProps) {\n const [isOpen, setIsOpen] = React.useState(\n props?.open ?? props?.defaultOpen ?? false,\n );\n\n React.useEffect(() => {\n if (props?.open !== undefined) setIsOpen(props.open);\n }, [props?.open]);\n\n const handleOpenChange = React.useCallback(\n (open: boolean) => {\n setIsOpen(open);\n props.onOpenChange?.(open);\n },\n [props],\n );\n\n return (\n <HoverCardContext.Provider value={{ isOpen }}>\n <HoverCardPrimitive.Root\n data-slot=\"hover-card\"\n {...props}\n onOpenChange={handleOpenChange}\n >\n {children}\n </HoverCardPrimitive.Root>\n </HoverCardContext.Provider>\n );\n}\n\ntype HoverCardTriggerProps = React.ComponentProps<\n typeof HoverCardPrimitive.Trigger\n>;\n\nfunction HoverCardTrigger(props: HoverCardTriggerProps) {\n return (\n <HoverCardPrimitive.Trigger data-slot=\"hover-card-trigger\" {...props} />\n );\n}\n\ntype HoverCardContentProps = React.ComponentProps<\n typeof HoverCardPrimitive.Content\n> &\n HTMLMotionProps<'div'> & {\n transition?: Transition;\n };\n\nfunction HoverCardContent({\n className,\n align = 'center',\n side = 'bottom',\n sideOffset = 4,\n transition = { type: 'spring', stiffness: 300, damping: 25 },\n children,\n ...props\n}: HoverCardContentProps) {\n const { isOpen } = useHoverCard();\n const initialPosition = getInitialPosition(side);\n\n return (\n <AnimatePresence>\n {isOpen && (\n <HoverCardPrimitive.Portal forceMount data-slot=\"hover-card-portal\">\n <HoverCardPrimitive.Content\n forceMount\n align={align}\n sideOffset={sideOffset}\n className=\"z-50\"\n {...props}\n >\n <motion.div\n key=\"hover-card-content\"\n data-slot=\"hover-card-content\"\n initial={{ opacity: 0, scale: 0.5, ...initialPosition }}\n animate={{ opacity: 1, scale: 1, x: 0, y: 0 }}\n exit={{ opacity: 0, scale: 0.5, ...initialPosition }}\n transition={transition}\n className={cn(\n 'w-64 rounded-lg border bg-popover p-4 text-popover-foreground shadow-md outline-none',\n className,\n )}\n {...props}\n >\n {children}\n </motion.div>\n </HoverCardPrimitive.Content>\n </HoverCardPrimitive.Portal>\n )}\n </AnimatePresence>\n );\n}\n\nexport {\n HoverCard,\n HoverCardTrigger,\n HoverCardContent,\n useHoverCard,\n type HoverCardContextType,\n type HoverCardProps,\n type HoverCardTriggerProps,\n type HoverCardContentProps,\n};\n",
"type": "registry:ui",
"target": "components/animate-ui/radix/hover-card.tsx"
}
]
}