{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "envelope",
  "type": "registry:ui",
  "title": "Envelope",
  "description": "An envelope that opens to reveal a letter.",
  "files": [
    {
      "path": "ui/envelope.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nexport interface EnvelopeProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onToggle\"> {\n  /** The letter inside. Put the important bit at the top: that's what peeks out. */\n  children: React.ReactNode;\n  /** Something on the flap, e.g. a <WaxSeal />. It hides when the envelope opens. */\n  seal?: React.ReactNode;\n  /** Handwritten address on the front. */\n  to?: React.ReactNode;\n  color?: string;\n  letterColor?: string;\n  open?: boolean;\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n}\n\nconst NOISE =\n  \"url(\\\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='3' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 .1 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>\\\")\";\n\nexport function Envelope({\n  children,\n  seal,\n  to,\n  color = \"#e9dcc3\",\n  letterColor = \"#fffdf8\",\n  open: openProp,\n  defaultOpen = false,\n  onOpenChange,\n  className = \"\",\n  style,\n  ...props\n}: EnvelopeProps) {\n  const [inner, setInner] = React.useState(defaultOpen);\n  const open = openProp ?? inner;\n  const toggle = () => {\n    setInner(!open);\n    onOpenChange?.(!open);\n  };\n\n  const paper = (shade: string) => `${NOISE}, linear-gradient(${shade}, ${shade}), ${color}`;\n\n  return (\n    <div\n      role=\"button\"\n      tabIndex={0}\n      aria-expanded={open}\n      aria-label={open ? \"Close envelope\" : \"Open envelope\"}\n      onClick={toggle}\n      onKeyDown={(e) => {\n        if (e.key === \"Enter\" || e.key === \" \") {\n          e.preventDefault();\n          toggle();\n        }\n      }}\n      className={`relative w-full max-w-[420px] cursor-pointer select-none outline-none focus-visible:ring-2 focus-visible:ring-offset-4 ${className}`}\n      style={{ aspectRatio: \"7 / 5\", perspective: 1400, ...style }}\n      {...props}\n    >\n      {/* back */}\n      <div\n        className=\"absolute inset-0 rounded-[6px]\"\n        style={{ background: paper(\"rgb(0 0 0 / .14)\"), boxShadow: \"0 14px 28px -10px rgb(0 0 0 / .35), 0 2px 4px rgb(0 0 0 / .12)\" }}\n      />\n\n      {/* letter */}\n      <div\n        className=\"absolute inset-x-[5%] top-[4%] h-[92%] overflow-hidden rounded-[3px] p-[6%]\"\n        style={{\n          zIndex: 2,\n          background: `${NOISE}, ${letterColor}`,\n          color: \"#2a2620\",\n          boxShadow: \"0 1px 3px rgb(0 0 0 / .15)\",\n          transform: open ? \"translateY(-62%)\" : \"translateY(0)\",\n          transition: open ? \"transform .7s cubic-bezier(.3,.9,.4,1) .45s\" : \"transform .5s cubic-bezier(.6,0,.8,.4)\",\n        }}\n      >\n        {children}\n      </div>\n\n      {/* front pocket */}\n      <div\n        className=\"pointer-events-none absolute inset-0 rounded-[6px]\"\n        style={{\n          zIndex: 3,\n          background: paper(\"rgb(255 255 255 / .06)\"),\n          clipPath: \"polygon(0 0, 50% 54%, 100% 0, 100% 100%, 0 100%)\",\n          filter: \"drop-shadow(0 -1px 0 rgb(0 0 0 / .1))\",\n        }}\n      >\n        <div\n          className=\"absolute inset-0\"\n          style={{\n            background:\n              \"linear-gradient(to top right, transparent 49.6%, rgb(0 0 0 / .07) 50%, transparent 50.6%), linear-gradient(to top left, transparent 49.6%, rgb(0 0 0 / .07) 50%, transparent 50.6%)\",\n          }}\n        />\n        {to && (\n          <div\n            className=\"absolute inset-x-0 bottom-[12%] text-center text-[22px] leading-tight\"\n            style={{ fontFamily: \"var(--font-hand, 'Bradley Hand', cursive)\", color: \"#2b2a5a\" }}\n          >\n            {to}\n          </div>\n        )}\n      </div>\n\n      {/* flap */}\n      <div\n        className=\"pointer-events-none absolute inset-x-0 top-0 h-[60%]\"\n        style={{\n          zIndex: open ? 1 : 4,\n          transformOrigin: \"top\",\n          transform: open ? \"rotateX(180deg)\" : \"rotateX(0deg)\",\n          transition: open\n            ? \"transform .55s cubic-bezier(.4,0,.2,1), z-index 0s .27s\"\n            : \"transform .55s cubic-bezier(.4,0,.2,1) .4s, z-index 0s .67s\",\n          filter: \"drop-shadow(0 2px 2px rgb(0 0 0 / .14))\",\n        }}\n      >\n        <div\n          className=\"absolute inset-0 rounded-t-[6px]\"\n          style={{ background: paper(\"rgb(0 0 0 / .03)\"), clipPath: \"polygon(0 0, 100% 0, 50% 100%)\" }}\n        />\n      </div>\n\n      {seal && (\n        <div\n          className=\"pointer-events-none absolute left-1/2 top-[60%]\"\n          style={{\n            zIndex: 5,\n            transform: `translate(-50%, -50%) scale(${open ? 0.6 : 1})`,\n            opacity: open ? 0 : 1,\n            transition: open ? \"all .25s ease-in\" : \"all .3s ease-out .8s\",\n          }}\n        >\n          {seal}\n        </div>\n      )}\n    </div>\n  );\n}\n"
    }
  ],
  "categories": [
    "extras"
  ]
}