{"$schema":"https://ui.shadcn.com/schema/registry-item.json","author":"Eduardo Calvo <educlopez93@gmail.com>","css":{},"dependencies":["motion","lucide-react","react-use-measure"],"description":"A ImageMetadataPreview component for SmoothUI.","devDependencies":[],"files":[{"content":"\"use client\";\n\nimport { ChevronUp, CircleX, Share } from \"lucide-react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { useEffect, useState } from \"react\";\nimport useMeasure from \"react-use-measure\";\n\nexport interface ImageMetadata {\n  by: string;\n  created: string;\n  source: string;\n  updated: string;\n}\n\nexport interface ImageMetadataPreviewProps {\n  alt?: string;\n  description?: string;\n  filename?: string;\n  imageSrc: string;\n  metadata: ImageMetadata;\n  onShare?: () => void;\n}\n\nexport default function ImageMetadataPreview({\n  imageSrc,\n  alt = \"Image preview\",\n  filename = \"screenshot.png\",\n  description = \"No description\",\n  metadata,\n  onShare,\n}: ImageMetadataPreviewProps) {\n  const [openInfo, setopenInfo] = useState(false);\n  const [isHoverDevice, setIsHoverDevice] = useState(false);\n  const [elementRef, bounds] = useMeasure();\n  const shouldReduceMotion = useReducedMotion();\n\n  useEffect(() => {\n    const mediaQuery = window.matchMedia(\"(hover: hover) and (pointer: fine)\");\n    setIsHoverDevice(mediaQuery.matches);\n\n    const handleChange = (e: MediaQueryListEvent) => {\n      setIsHoverDevice(e.matches);\n    };\n\n    mediaQuery.addEventListener(\"change\", handleChange);\n    return () => mediaQuery.removeEventListener(\"change\", handleChange);\n  }, []);\n\n  const handleClickOpen = () => {\n    setopenInfo((b) => !b);\n  };\n\n  const handleClickClose = () => {\n    setopenInfo((b) => !b);\n  };\n\n  return (\n    <div className=\"absolute bottom-10 flex flex-col items-center justify-center gap-4\">\n      <motion.div\n        animate={shouldReduceMotion ? {} : { y: -bounds.height }}\n        className=\"pointer-events-none overflow-hidden rounded-xl\"\n        transition={shouldReduceMotion ? { duration: 0 } : { duration: 0.25 }}\n      >\n        <img\n          alt={alt}\n          draggable={false}\n          height={437}\n          src={imageSrc}\n          width={300}\n        />\n      </motion.div>\n\n      <div className=\"relative flex w-full flex-col items-center gap-4\">\n        <div className=\"relative flex w-full flex-row items-center justify-center gap-4\">\n          <button\n            aria-label=\"Share\"\n            className={`min-h-[44px] min-w-[44px] rounded-full border bg-background p-3 transition focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${\n              isHoverDevice ? \"hover:bg-muted\" : \"\"\n            }`}\n            disabled={!onShare}\n            onClick={onShare}\n            type=\"button\"\n          >\n            <Share aria-hidden=\"true\" size={16} />\n          </button>\n          <button\n            aria-label=\"Connect\"\n            className=\"min-h-[44px] cursor-not-allowed rounded-full border bg-background px-4 py-3 text-sm transition focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50\"\n            disabled\n            type=\"button\"\n          >\n            Connect\n          </button>\n          <AnimatePresence>\n            {openInfo ? null : (\n              <motion.button\n                animate={\n                  shouldReduceMotion\n                    ? { opacity: 1 }\n                    : { filter: \"blur(0px)\", opacity: 1 }\n                }\n                aria-label=\"Open Metadata Preview\"\n                className={`min-h-[44px] min-w-[44px] border bg-background p-3 shadow-xs transition focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${\n                  isHoverDevice ? \"hover:bg-muted\" : \"\"\n                }`}\n                initial={\n                  shouldReduceMotion\n                    ? { opacity: 1 }\n                    : { filter: \"blur(4px)\", opacity: 0 }\n                }\n                onClick={handleClickOpen}\n                style={{ borderRadius: 100 }}\n                transition={\n                  shouldReduceMotion ? { duration: 0 } : { duration: 0.2 }\n                }\n              >\n                <ChevronUp aria-hidden=\"true\" size={16} />\n              </motion.button>\n            )}\n          </AnimatePresence>\n        </div>\n        <AnimatePresence>\n          {openInfo ? (\n            <motion.div\n              animate={\n                shouldReduceMotion\n                  ? { opacity: 1 }\n                  : { filter: \"blur(0px)\", opacity: 1 }\n              }\n              className=\"absolute bottom-0 w-full cursor-pointer gap-4 border bg-background p-5 shadow-xs\"\n              initial={\n                shouldReduceMotion\n                  ? { opacity: 1 }\n                  : { filter: \"blur(4px)\", opacity: 0 }\n              }\n              onClick={handleClickClose}\n              style={{ borderRadius: 20 }}\n              transition={\n                shouldReduceMotion\n                  ? { duration: 0 }\n                  : { bounce: 0, duration: 0.25, type: \"spring\" as const }\n              }\n            >\n              <div className=\"flex flex-col items-start\" ref={elementRef}>\n                <div className=\"flex w-full flex-row items-start justify-between gap-4\">\n                  <div>\n                    <p className=\"text-foreground\">{filename}</p>\n                    <p className=\"text-primary-foreground\">{description}</p>\n                  </div>\n\n                  <button\n                    aria-label=\"Close metadata preview\"\n                    className={`flex min-h-[44px] min-w-[44px] items-center justify-center rounded p-2 transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${\n                      isHoverDevice ? \"hover:bg-muted\" : \"\"\n                    }`}\n                    onClick={(e) => {\n                      e.stopPropagation();\n                      handleClickClose();\n                    }}\n                    type=\"button\"\n                  >\n                    <CircleX aria-hidden=\"true\" size={16} />\n                  </button>\n                </div>\n                <table className=\"flex w-full flex-col items-center gap-4 text-foreground\">\n                  <tbody className=\"w-full\">\n                    <tr className=\"flex w-full flex-row items-center gap-4\">\n                      <td className=\"w-1/2\">Created</td>\n                      <td className=\"w-1/2 text-primary-foreground\">\n                        {metadata.created}\n                      </td>\n                    </tr>\n                    <tr className=\"flex w-full flex-row items-center gap-4\">\n                      <td className=\"w-1/2\">Updated</td>\n                      <td className=\"w-1/2 text-primary-foreground\">\n                        {metadata.updated}\n                      </td>\n                    </tr>\n                    <tr className=\"flex w-full flex-row items-center gap-4\">\n                      <td className=\"w-1/2\">By</td>\n                      <td className=\"w-1/2\">{metadata.by}</td>\n                    </tr>\n                    <tr className=\"flex w-full flex-row items-center gap-4\">\n                      <td className=\"w-1/2\">Source</td>\n                      <td className=\"w-1/2 truncate\">{metadata.source}</td>\n                    </tr>\n                  </tbody>\n                </table>\n              </div>\n            </motion.div>\n          ) : null}\n        </AnimatePresence>\n      </div>\n    </div>\n  );\n}\n","path":"index.tsx","target":"components/smoothui/image-metadata-preview/index.tsx","type":"registry:ui"}],"name":"image-metadata-preview","registryDependencies":[],"title":"Image Metadata Preview","type":"registry:ui"}