{
  "$schema": "https://blode.co/ui/schema/registry-item.json",
  "name": "share",
  "title": "Share",
  "author": "Matthew Blode",
  "description": "A button that opens the native share sheet and falls back to copying the link.",
  "dependencies": ["blode-icons-react", "motion", "sonner"],
  "registryDependencies": ["button", "@blode/use-copy-to-clipboard"],
  "files": [
    {
      "path": "ui/share.tsx",
      "content": "\"use client\";\n\nimport { CheckIcon, ShareIcon } from \"blode-icons-react\";\nimport { AnimatePresence, motion } from \"motion/react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport type * as React from \"react\";\nimport { toast } from \"sonner\";\n\nimport { cn } from \"@/lib/utils\";\nimport { useCopyToClipboard } from \"@/hooks/use-copy-to-clipboard\";\nimport { Button } from \"@/components/ui/button\";\n\nexport interface ShareProps extends Omit<\n  React.ComponentProps<typeof Button>,\n  \"children\" | \"onClick\"\n> {\n  /** Message shown when the share sheet is unavailable and the value is copied instead */\n  copyMessage?: string;\n  /** Accessible label, also the visible text when `showLabel` is set (default: \"Share\") */\n  label?: string;\n  /** Callback when the value was shared or copied */\n  onShare?: () => void;\n  /** Render `label` as visible text beside the icon (default: false) */\n  showLabel?: boolean;\n  /** Body text passed to the share sheet */\n  text?: string;\n  /** Timeout in ms before resetting (default: 2000) */\n  timeout?: number;\n  /** Title passed to the share sheet */\n  title?: string;\n  /** The URL or text to share */\n  value: string;\n}\n\nconst isDismissal = (error: unknown) => {\n  const name = (error as { name?: string } | null)?.name;\n  return name === \"AbortError\" || name === \"NotAllowedError\";\n};\n\nconst Share = ({\n  className,\n  copyMessage = \"Link copied to clipboard\",\n  label = \"Share\",\n  onShare,\n  showLabel = false,\n  size = showLabel ? \"default\" : \"icon\",\n  text,\n  timeout = 2000,\n  title,\n  value,\n  variant = \"outline\",\n  ...props\n}: ShareProps) => {\n  const [isShared, setIsShared] = useState(false);\n  const { copyToClipboard, isCopied } = useCopyToClipboard({ onCopy: onShare, timeout });\n  // oxlint-disable-next-line unicorn/no-useless-undefined -- React 19's useRef requires an explicit initial value\n  const resetRef = useRef<number | undefined>(undefined);\n  const isDone = isShared || isCopied;\n\n  useEffect(() => () => window.clearTimeout(resetRef.current), []);\n\n  const handleShare = async () => {\n    const data: ShareData = { text, title, url: value };\n    const canUseShareSheet =\n      typeof navigator !== \"undefined\" &&\n      typeof navigator.share === \"function\" &&\n      (typeof navigator.canShare !== \"function\" || navigator.canShare(data));\n\n    if (canUseShareSheet) {\n      try {\n        await navigator.share(data);\n        setIsShared(true);\n        onShare?.();\n\n        if (timeout !== 0) {\n          window.clearTimeout(resetRef.current);\n          resetRef.current = window.setTimeout(() => setIsShared(false), timeout);\n        }\n\n        return;\n      } catch (error) {\n        // The share sheet was dismissed, or the gesture was rejected — say nothing.\n        if (isDismissal(error)) {\n          return;\n        }\n      }\n    }\n\n    await copyToClipboard(value);\n    toast.success(copyMessage);\n  };\n\n  return (\n    <Button\n      aria-label={showLabel ? undefined : label}\n      className={cn(\"shrink-0\", className)}\n      data-shared={isDone}\n      data-slot=\"share\"\n      onClick={handleShare}\n      size={size}\n      variant={variant}\n      {...props}\n    >\n      <AnimatePresence initial={false} mode=\"wait\">\n        <motion.span\n          animate={{ opacity: 1, scale: 1 }}\n          className=\"flex items-center justify-center\"\n          data-icon={showLabel ? \"inline-start\" : undefined}\n          exit={{ opacity: 0, scale: 0.8 }}\n          initial={{ opacity: 0, scale: 0.8 }}\n          key={isDone ? \"check\" : \"share\"}\n          transition={{ duration: 0.15 }}\n        >\n          {isDone ? <CheckIcon /> : <ShareIcon />}\n        </motion.span>\n      </AnimatePresence>\n      {showLabel ? label : null}\n    </Button>\n  );\n};\n\nexport { Share };\n",
      "type": "registry:ui",
      "target": ""
    }
  ],
  "type": "registry:ui"
}
