{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "polaroid",
  "type": "registry:ui",
  "title": "Polaroid",
  "description": "An instant photo that develops as it scrolls into view. Click to shake it.",
  "files": [
    {
      "path": "ui/polaroid.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nexport interface PolaroidProps extends React.HTMLAttributes<HTMLDivElement> {\n  src?: string;\n  alt?: string;\n  caption?: React.ReactNode;\n  rotate?: number;\n  tape?: boolean;\n  /** Develop from a murky brown into the full photo when scrolled into view. Click to shake it again. */\n  develop?: boolean;\n  /** Develop time in ms. */\n  duration?: number;\n  width?: number;\n  /** Custom content instead of an image. */\n  children?: React.ReactNode;\n}\n\nconst CSS = `\n@keyframes eph-develop-img {\n  0% { filter: sepia(.9) saturate(.2) brightness(.35) contrast(.55) blur(1.5px); }\n  60% { filter: sepia(.35) saturate(.7) brightness(.85) contrast(.85) blur(.3px); }\n  100% { filter: none; }\n}\n@keyframes eph-develop-veil { 0% { opacity: 1; } 100% { opacity: 0; } }\n@keyframes eph-shake {\n  0%,100% { transform: none; }\n  20% { transform: rotate(-3deg) translateX(-6px); }\n  40% { transform: rotate(3deg) translateX(6px); }\n  60% { transform: rotate(-2deg) translateX(-4px); }\n  80% { transform: rotate(1deg) translateX(2px); }\n}\n.eph-undeveloped img, .eph-undeveloped .eph-media { filter: sepia(.9) saturate(.2) brightness(.35) contrast(.55) blur(1.5px); }\n@media (prefers-reduced-motion: reduce) {\n  .eph-undeveloped img, .eph-undeveloped .eph-media { filter: none; }\n  .eph-polaroid * { animation: none !important; }\n  .eph-veil { display: none; }\n}\n`;\n\nconst TAPE =\n  \"conic-gradient(from 45deg at left, #0000, #000 1deg 89deg, #0000 90deg) left / 51% 8px repeat-y, conic-gradient(from -135deg at right, #0000, #000 1deg 89deg, #0000 90deg) right / 51% 8px repeat-y\";\n\nfunction useInView<T extends Element>() {\n  const ref = React.useRef<T>(null);\n  const [inView, setInView] = React.useState(false);\n  React.useEffect(() => {\n    const el = ref.current;\n    if (!el) return;\n    if (typeof IntersectionObserver === \"undefined\") return setInView(true);\n    const io = new IntersectionObserver(\n      ([entry]) => {\n        if (entry.isIntersecting) {\n          setInView(true);\n          io.disconnect();\n        }\n      },\n      { threshold: 0.3 },\n    );\n    io.observe(el);\n    return () => io.disconnect();\n  }, []);\n  return [ref, inView] as const;\n}\n\nexport function Polaroid({\n  src,\n  alt = \"\",\n  caption,\n  rotate = -2,\n  tape = false,\n  develop = true,\n  duration = 4000,\n  width = 260,\n  children,\n  className = \"\",\n  style,\n  onClick,\n  ...props\n}: PolaroidProps) {\n  const [ref, inView] = useInView<HTMLDivElement>();\n  const [shakes, setShakes] = React.useState(0);\n  const running = develop && inView;\n  const anim = (name: string) => (running ? `${name} ${duration}ms cubic-bezier(.3,.1,.3,1) both` : undefined);\n\n  return (\n    <div\n      ref={ref}\n      className={`eph-polaroid relative inline-block align-top ${develop && !inView ? \"eph-undeveloped\" : \"\"} ${className}`}\n      style={{ transform: `rotate(${rotate}deg)`, width, maxWidth: \"100%\", ...style }}\n      onClick={(e) => {\n        if (develop) setShakes((s) => s + 1);\n        onClick?.(e);\n      }}\n      {...props}\n    >\n      <style href=\"eph-polaroid\" precedence=\"default\">\n        {CSS}\n      </style>\n      {/* keyed so a click restarts the shake and the develop animation */}\n      <div key={shakes} style={{ animation: shakes ? \"eph-shake .5s ease-in-out\" : undefined }}>\n      <div\n        className=\"p-[7%] pb-0\"\n        style={{\n          background: \"linear-gradient(160deg, #fffefb, #f3f1ea)\",\n          boxShadow: \"0 1px 1px rgb(0 0 0 / .12), 0 10px 22px -6px rgb(0 0 0 / .28), inset 0 0 0 1px rgb(0 0 0 / .03)\",\n        }}\n      >\n        <div className=\"relative aspect-square overflow-hidden\" style={{ background: \"#2b2620\" }}>\n          {children ? (\n            <div className=\"eph-media absolute inset-0\" style={{ animation: anim(\"eph-develop-img\") }}>\n              {children}\n            </div>\n          ) : (\n            src && (\n              // eslint-disable-next-line @next/next/no-img-element\n              <img\n                src={src}\n                alt={alt}\n                draggable={false}\n                className=\"absolute inset-0 h-full w-full object-cover\"\n                style={{ animation: anim(\"eph-develop-img\") }}\n              />\n            )\n          )}\n          {develop && (\n            <div\n              className=\"eph-veil pointer-events-none absolute inset-0\"\n              style={{\n                background: \"linear-gradient(135deg, #3a3d36, #2c2a25)\",\n                mixBlendMode: \"multiply\",\n                opacity: inView ? undefined : 1,\n                animation: running ? `eph-develop-veil ${duration * 0.8}ms ease-out both` : undefined,\n              }}\n            />\n          )}\n          <div\n            className=\"pointer-events-none absolute inset-0\"\n            style={{ boxShadow: \"inset 0 0 0 1px rgb(0 0 0 / .15), inset 0 0 24px rgb(0 0 0 / .18)\" }}\n          />\n        </div>\n        <div\n          className=\"flex min-h-[64px] items-center justify-center px-2 py-3 text-center text-[24px] leading-tight\"\n          style={{ fontFamily: \"var(--font-hand, 'Bradley Hand', 'Segoe Print', cursive)\", color: \"#2b2a5a\" }}\n        >\n          {caption}\n        </div>\n      </div>\n      </div>\n      {tape && (\n        <div\n          aria-hidden\n          className=\"absolute left-1/2 top-0 h-7 w-24\"\n          style={{\n            transform: \"translate(-50%, -45%) rotate(-4deg)\",\n            background: \"linear-gradient(180deg, rgb(238 226 196 / .78), rgb(226 212 178 / .72))\",\n            mask: TAPE,\n            WebkitMask: TAPE,\n          }}\n        />\n      )}\n    </div>\n  );\n}\n"
    }
  ],
  "categories": [
    "extras"
  ]
}