{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "preview-link-card",
  "dependencies": [
    "motion",
    "radix-ui"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/components/watermelon-ui/preview-link-card.tsx",
      "content": "'use client';\n\n\nimport * as React from 'react';\nimport { HoverCard as HoverCardPrime } from 'radix-ui';\nimport {\n  AnimatePresence,\n  motion,\n  useMotionValue,\n  useSpring,\n  type MotionValue,\n  type HTMLMotionProps,\n  type SpringOptions,\n} from 'motion/react';\nimport { cn } from '@/lib/utils';\n\ntype PreviewLinkCardProps = PreviewLinkCardPrimitiveProps;\n\nfunction PreviewLinkCard(props: PreviewLinkCardProps) {\n  return <PreviewLinkCardPrimitive {...props} />;\n}\n\ntype PreviewLinkCardTriggerProps = PreviewLinkCardTriggerPrimitiveProps;\n\nfunction PreviewLinkCardTrigger(props: PreviewLinkCardTriggerProps) {\n  return <PreviewLinkCardTriggerPrimitive {...props} />;\n}\n\ntype PreviewLinkCardContentProps = PreviewLinkCardContentPrimitiveProps;\n\nfunction PreviewLinkCardContent({\n  className,\n  align = 'center',\n  sideOffset = 4,\n  ...props\n}: PreviewLinkCardContentProps) {\n  return (\n    <PreviewLinkCardPortalPrimitive>\n      <PreviewLinkCardContentPrimitive\n        align={align}\n        sideOffset={sideOffset}\n        className={cn(\n          'z-50 origin-(--radix-hover-card-content-transform-origin) rounded-md border shadow-md outline-hidden overflow-hidden',\n          className,\n        )}\n        {...props}\n      />\n    </PreviewLinkCardPortalPrimitive>\n  );\n}\n\ntype PreviewLinkCardImageProps = PreviewLinkCardImagePrimitiveProps;\n\nfunction PreviewLinkCardImage(props: PreviewLinkCardImageProps) {\n  return <PreviewLinkCardImagePrimitive {...props} />;\n}\n\nexport {\n  PreviewLinkCard,\n  PreviewLinkCardTrigger,\n  PreviewLinkCardContent,\n  PreviewLinkCardImage,\n  type PreviewLinkCardProps,\n  type PreviewLinkCardTriggerProps,\n  type PreviewLinkCardContentProps,\n  type PreviewLinkCardImageProps,\n};\n\ntype PreviewLinkCardContextType = {\n  href: string;\n  src?: string;\n  width?: number;\n  height?: number;\n};\n\nconst [PreviewLinkCardProvider, usePreviewLinkCard] =\n  getStrictContext<PreviewLinkCardContextType>('PreviewLinkCardContext');\n\ntype PreviewLinkCardPrimitiveProps = HoverCardPrimitiveProps & {\n  href: string;\n  src?: string;\n  width?: number;\n  height?: number;\n  deviceScaleFactor?: number;\n  colorScheme?: 'light' | 'dark';\n};\n\nfunction PreviewLinkCardPrimitive({\n  href,\n  src,\n  width = 240,\n  height = 135,\n  deviceScaleFactor = 1,\n  colorScheme = 'light',\n  ...props\n}: PreviewLinkCardProps) {\n  const imageSrc =\n    src ??\n    `https://api.microlink.io/?${buildQueryString({\n      url: href,\n      screenshot: true,\n      meta: false,\n      embed: 'screenshot.url',\n      colorScheme,\n      'viewport.isMobile': true,\n      'viewport.deviceScaleFactor': deviceScaleFactor,\n      'viewport.width': width * 3,\n      'viewport.height': height * 3,\n    })}`;\n\n  React.useEffect(() => {\n    const link = document.createElement('link');\n    link.rel = 'preload';\n    link.as = 'image';\n    link.href = imageSrc;\n    document.head.appendChild(link);\n    return () => {\n      document.head.removeChild(link);\n    };\n  }, [imageSrc]);\n\n  return (\n    <PreviewLinkCardProvider value={{ href, src: imageSrc, width, height }}>\n      <HoverCardPrimitive data-slot=\"preview-link-card\" {...props} />\n    </PreviewLinkCardProvider>\n  );\n}\n\ntype PreviewLinkCardTriggerPrimitiveProps = HoverCardTriggerPrimitiveProps &\n  React.ComponentProps<'a'>;\n\nfunction PreviewLinkCardTriggerPrimitive({\n  asChild,\n  children,\n  href: hrefProp,\n  ...props\n}: PreviewLinkCardTriggerProps) {\n  const { href } = usePreviewLinkCard();\n\n  return (\n    <HoverCardTriggerPrimitive\n      data-slot=\"preview-link-card-trigger\"\n      asChild\n      {...props}\n    >\n      {asChild ? children : <a href={hrefProp ?? href}>{children}</a>}\n    </HoverCardTriggerPrimitive>\n  );\n}\n\ntype PreviewLinkCardPortalProps = HoverCardPortalPrimitiveProps;\n\nfunction PreviewLinkCardPortalPrimitive(props: PreviewLinkCardPortalProps) {\n  return (\n    <HoverCardPortalPrimitive data-slot=\"preview-link-card-portal\" {...props} />\n  );\n}\n\nfunction buildQueryString(\n  params: Record<string, string | number | boolean | undefined | null>,\n) {\n  const sp = new URLSearchParams();\n  for (const [k, v] of Object.entries(params)) {\n    if (v === undefined || v === null) continue;\n    sp.append(k, String(v));\n  }\n  return sp.toString();\n}\n\ntype PreviewLinkCardContentPrimitiveProps = HoverCardContentProps &\n  React.ComponentProps<'a'>;\n\nfunction PreviewLinkCardContentPrimitive({\n  side = 'top',\n  sideOffset = 10,\n  align = 'center',\n  alignOffset,\n  avoidCollisions,\n  collisionBoundary,\n  collisionPadding,\n  arrowPadding,\n  sticky,\n  hideWhenDetached,\n  transition = { type: 'spring', stiffness: 300, damping: 25 },\n  asChild,\n  children,\n  href: hrefProp,\n  style,\n  ...props\n}: PreviewLinkCardContentProps) {\n  const { href } = usePreviewLinkCard();\n\n  return (\n    <HoverCardContentPrimitive\n      data-slot=\"preview-link-card-content\"\n      side={side}\n      sideOffset={sideOffset}\n      align={align}\n      alignOffset={alignOffset}\n      avoidCollisions={avoidCollisions}\n      collisionBoundary={collisionBoundary}\n      collisionPadding={collisionPadding}\n      arrowPadding={arrowPadding}\n      sticky={sticky}\n      hideWhenDetached={hideWhenDetached}\n      transition={transition}\n      asChild={asChild}\n      {...(asChild ? { style, ...props } : {})}\n    >\n      {asChild ? (\n        children\n      ) : (\n        <a\n          style={{\n            display: 'block',\n            ...style,\n          }}\n          href={hrefProp ?? href}\n          {...props}\n        >\n          {children}\n        </a>\n      )}\n    </HoverCardContentPrimitive>\n  );\n}\n\ntype PreviewLinkCardImagePrimitiveProps = Omit<\n  React.ComponentProps<'img'>,\n  'src' | 'width' | 'height'\n>;\n\nfunction PreviewLinkCardImagePrimitive({\n  alt = 'preview image',\n  ...props\n}: PreviewLinkCardImageProps) {\n  const { src, width, height } = usePreviewLinkCard();\n\n  return <img src={src} width={width} height={height} alt={alt} {...props} />;\n}\n\n\ntype HoverCardContextType = {\n  isOpen: boolean;\n  setIsOpen: (isOpen: boolean) => void;\n  x: MotionValue<number>;\n  y: MotionValue<number>;\n  followCursor?: boolean | 'x' | 'y';\n  followCursorSpringOptions?: SpringOptions;\n};\n\nconst [HoverCardProvider, useHoverCard] =\n  getStrictContext<HoverCardContextType>('HoverCardContext');\n\ntype HoverCardPrimitiveProps = React.ComponentProps<typeof HoverCardPrime.Root> & {\n  followCursor?: boolean | 'x' | 'y';\n  followCursorSpringOptions?: SpringOptions;\n};\n\nfunction HoverCardPrimitive({\n  followCursor = false,\n  followCursorSpringOptions = { stiffness: 200, damping: 17 },\n  ...props\n}: HoverCardPrimitiveProps) {\n  const [isOpen, setIsOpen] = useControlledState({\n    value: props?.open,\n    defaultValue: props?.defaultOpen,\n    onChange: props?.onOpenChange,\n  });\n  const x = useMotionValue(0);\n  const y = useMotionValue(0);\n\n  return (\n    <HoverCardProvider\n      value={{\n        isOpen,\n        setIsOpen,\n        x,\n        y,\n        followCursor,\n        followCursorSpringOptions,\n      }}\n    >\n      <HoverCardPrime.Root\n        data-slot=\"hover-card\"\n        {...props}\n        onOpenChange={setIsOpen}\n      />\n    </HoverCardProvider>\n  );\n}\n\ntype HoverCardTriggerPrimitiveProps = React.ComponentProps<\n  typeof HoverCardPrime.Trigger\n>;\n\nfunction HoverCardTriggerPrimitive({ onMouseMove, ...props }: HoverCardTriggerPrimitiveProps) {\n  const { x, y, followCursor } = useHoverCard();\n\n  const handleMouseMove = (event: React.MouseEvent<HTMLAnchorElement>) => {\n    onMouseMove?.(event);\n\n    const target = event.currentTarget.getBoundingClientRect();\n\n    if (followCursor === 'x' || followCursor === true) {\n      const eventOffsetX = event.clientX - target.left;\n      const offsetXFromCenter = (eventOffsetX - target.width / 2) / 2;\n      x.set(offsetXFromCenter);\n    }\n\n    if (followCursor === 'y' || followCursor === true) {\n      const eventOffsetY = event.clientY - target.top;\n      const offsetYFromCenter = (eventOffsetY - target.height / 2) / 2;\n      y.set(offsetYFromCenter);\n    }\n  };\n\n  return (\n    <HoverCardPrime.Trigger\n      data-slot=\"hover-card-trigger\"\n      onMouseMove={handleMouseMove}\n      {...props}\n    />\n  );\n}\n\ntype HoverCardPortalPrimitiveProps = Omit<\n  React.ComponentProps<typeof HoverCardPrime.Portal>,\n  'forceMount'\n>;\n\nfunction HoverCardPortalPrimitive(props: HoverCardPortalPrimitiveProps) {\n  const { isOpen } = useHoverCard();\n\n  return (\n    <AnimatePresence>\n      {isOpen && (\n        <HoverCardPrime.Portal\n          forceMount\n          data-slot=\"hover-card-portal\"\n          {...props}\n        />\n      )}\n    </AnimatePresence>\n  );\n}\n\ntype HoverCardContentProps = React.ComponentProps<\n  typeof HoverCardPrime.Content\n> &\n  HTMLMotionProps<'div'>;\n\nfunction HoverCardContentPrimitive({\n  align,\n  alignOffset,\n  side,\n  sideOffset,\n  avoidCollisions,\n  collisionBoundary,\n  collisionPadding,\n  arrowPadding,\n  sticky,\n  hideWhenDetached,\n  style,\n  transition = { type: 'spring', stiffness: 300, damping: 25 },\n  ...props\n}: HoverCardContentProps) {\n  const { x, y, followCursor, followCursorSpringOptions } = useHoverCard();\n  const translateX = useSpring(x, followCursorSpringOptions);\n  const translateY = useSpring(y, followCursorSpringOptions);\n\n  return (\n    <HoverCardPrime.Content\n      asChild\n      forceMount\n      align={align}\n      alignOffset={alignOffset}\n      side={side}\n      sideOffset={sideOffset}\n      avoidCollisions={avoidCollisions}\n      collisionBoundary={collisionBoundary}\n      collisionPadding={collisionPadding}\n      arrowPadding={arrowPadding}\n      sticky={sticky}\n      hideWhenDetached={hideWhenDetached}\n    >\n      <motion.div\n        key=\"hover-card-content\"\n        data-slot=\"hover-card-content\"\n        initial={{ opacity: 0, scale: 0.5 }}\n        animate={{ opacity: 1, scale: 1 }}\n        exit={{ opacity: 0, scale: 0.5 }}\n        transition={transition}\n        style={{\n          x:\n            followCursor === 'x' || followCursor === true\n              ? translateX\n              : undefined,\n          y:\n            followCursor === 'y' || followCursor === true\n              ? translateY\n              : undefined,\n          ...style,\n        }}\n        {...props}\n      />\n    </HoverCardPrime.Content>\n  );\n}\n\n\n\nfunction getStrictContext<T>(\n  name?: string,\n): readonly [\n  ({\n    value,\n    children,\n  }: {\n    value: T;\n    children?: React.ReactNode;\n  }) => React.JSX.Element,\n  () => T,\n] {\n  const Context = React.createContext<T | undefined>(undefined);\n\n  const Provider = ({\n    value,\n    children,\n  }: {\n    value: T;\n    children?: React.ReactNode;\n  }) => <Context.Provider value={value}>{children}</Context.Provider>;\n\n  const useSafeContext = () => {\n    const ctx = React.useContext(Context);\n    if (ctx === undefined) {\n      throw new Error(`useContext must be used within ${name ?? 'a Provider'}`);\n    }\n    return ctx;\n  };\n\n  return [Provider, useSafeContext] as const;\n}\n\nexport { getStrictContext };\n\n\ninterface CommonControlledStateProps<T> {\n  value?: T;\n  defaultValue?: T;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function useControlledState<T, Rest extends any[] = []>(\n  props: CommonControlledStateProps<T> & {\n    onChange?: (value: T, ...args: Rest) => void;\n  },\n): readonly [T, (next: T, ...args: Rest) => void] {\n  const { value, defaultValue, onChange } = props;\n\n  const [state, setInternalState] = React.useState<T>(\n    value !== undefined ? value : (defaultValue as T),\n  );\n\n  React.useEffect(() => {\n    if (value !== undefined) setInternalState(value);\n  }, [value]);\n\n  const setState = React.useCallback(\n    (next: T, ...args: Rest) => {\n      setInternalState(next);\n      onChange?.(next, ...args);\n    },\n    [onChange],\n  );\n\n  return [state, setState] as const;\n}\n",
      "type": "registry:ui",
      "target": "components/ui/preview-link-card.tsx"
    }
  ],
  "type": "registry:ui"
}
