{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "long-shadow",
  "title": "Long Shadow",
  "description": "A flat icon casts a long shadow to the frame edge, then the light swings and the shadow sweeps around on a spring.",
  "dependencies": ["remotion"],
  "files": [
    {
      "path": "registry/remocn/long-shadow/index.tsx",
      "content": "\"use client\";\n\nimport { type ReactNode, useId } from \"react\";\nimport {\n  AbsoluteFill,\n  Easing,\n  interpolate,\n  spring,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nexport type LongShadowProps = {\n  /** Flat icon or mark that casts the shadow. Defaults to a red check badge. */\n  children?: ReactNode;\n  /** Shadow direction in degrees at the start (0 = right, 90 = down). */\n  angleFrom?: number;\n  /** Shadow direction after the light swings. */\n  angleTo?: number;\n  shadowColor?: string;\n  background?: string;\n  /** Fade the shadow toward its far end, 0 to 1. */\n  fade?: number;\n  /** Colors of the default badge. */\n  iconColor?: string;\n  checkColor?: string;\n  speed?: number;\n};\n\nexport const longShadowDefaults = {\n  angleFrom: 45,\n  angleTo: 135,\n  shadowColor: \"#ECEAE6\",\n  background: \"#FFFFFF\",\n  fade: 0,\n  iconColor: \"#E5322D\",\n  checkColor: \"#FFFFFF\",\n  speed: 1,\n};\n\nexport const longShadowLength = 48;\n\n// Log-doubling extrusion: each pass unions the silhouette with itself shifted by\n// twice the previous distance, so 10 passes stack 1024 copies.\nconst PASSES = 10;\nconst COPIES = 2 ** PASSES - 1;\n\nconst clamp = {\n  extrapolateLeft: \"clamp\" as const,\n  extrapolateRight: \"clamp\" as const,\n};\n\nfunction Badge({\n  size,\n  iconColor,\n  checkColor,\n}: {\n  size: number;\n  iconColor: string;\n  checkColor: string;\n}) {\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 100 100\" aria-hidden>\n      <circle cx={50} cy={50} r={49} fill={iconColor} />\n      <path\n        d=\"M 29 52 L 44 66 L 72 36\"\n        fill=\"none\"\n        stroke={checkColor}\n        strokeWidth={11}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function LongShadow({\n  children,\n  angleFrom = longShadowDefaults.angleFrom,\n  angleTo = longShadowDefaults.angleTo,\n  shadowColor = longShadowDefaults.shadowColor,\n  background = longShadowDefaults.background,\n  fade = longShadowDefaults.fade,\n  iconColor = longShadowDefaults.iconColor,\n  checkColor = longShadowDefaults.checkColor,\n  speed = longShadowDefaults.speed,\n}: LongShadowProps) {\n  const frame = useCurrentFrame();\n  const { width, height, fps } = useVideoConfig();\n  const id = useId().replace(/[^a-zA-Z0-9_-]/g, \"\");\n  const t = (frame / fps) * 30 * speed;\n  const unit = Math.min(width, height) / 720;\n\n  const pop = spring({\n    frame: t,\n    fps: 30,\n    config: { damping: 13, stiffness: 170, mass: 0.8 },\n  });\n  const grow = interpolate(t, [7, 22], [0, 1], {\n    ...clamp,\n    easing: Easing.out(Easing.cubic),\n  });\n  const swing = spring({\n    frame: t - 24,\n    fps: 30,\n    config: { damping: 11, stiffness: 70, mass: 0.9 },\n  });\n  const angle = angleFrom + (angleTo - angleFrom) * swing;\n  const rad = (angle * Math.PI) / 180;\n  const length = Math.hypot(width, height) * grow;\n  const step = length / COPIES;\n  const dx = Math.cos(rad) * step;\n  const dy = Math.sin(rad) * step;\n\n  const content = children ?? (\n    <Badge\n      size={Math.min(width, height) * 0.34}\n      iconColor={iconColor}\n      checkColor={checkColor}\n    />\n  );\n  const mask =\n    fade > 0\n      ? `linear-gradient(${angle + 90}deg, #000 50%, rgba(0,0,0,${1 - Math.min(1, fade)}) 100%)`\n      : undefined;\n\n  return (\n    <AbsoluteFill style={{ backgroundColor: background, overflow: \"hidden\" }}>\n      <svg width={0} height={0} style={{ position: \"absolute\" }} aria-hidden>\n        <filter\n          id={`${id}-extrude`}\n          x=\"0\"\n          y=\"0\"\n          width=\"1\"\n          height=\"1\"\n          colorInterpolationFilters=\"sRGB\"\n        >\n          <feColorMatrix\n            in=\"SourceAlpha\"\n            type=\"matrix\"\n            values=\"0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 3 0\"\n            result=\"m0\"\n          />\n          {Array.from({ length: PASSES }, (_, i) => (\n            <feOffset\n              key={`o${i}`}\n              in={`m${i}`}\n              dx={dx * 2 ** i}\n              dy={dy * 2 ** i}\n              result={`o${i}`}\n            />\n          )).flatMap((offset, i) => [\n            offset,\n            <feMerge key={`m${i}`} result={`m${i + 1}`}>\n              <feMergeNode in={`m${i}`} />\n              <feMergeNode in={`o${i}`} />\n            </feMerge>,\n          ])}\n          {/* feOffset snaps to whole pixels; a hair of blur hides the steps. */}\n          <feGaussianBlur\n            in={`m${PASSES}`}\n            stdDeviation={0.8 * unit}\n            result=\"soft\"\n          />\n          <feFlood floodColor={shadowColor} />\n          <feComposite in2=\"soft\" operator=\"in\" />\n        </filter>\n      </svg>\n      <AbsoluteFill\n        style={{\n          transform: `scale(${pop})`,\n          alignItems: \"center\",\n          justifyContent: \"center\",\n        }}\n      >\n        <AbsoluteFill\n          style={{\n            alignItems: \"center\",\n            justifyContent: \"center\",\n            filter: `url(#${id}-extrude)`,\n            opacity: grow > 0 ? 1 : 0,\n            maskImage: mask,\n            WebkitMaskImage: mask,\n          }}\n        >\n          {content}\n        </AbsoluteFill>\n        <AbsoluteFill\n          style={{ alignItems: \"center\", justifyContent: \"center\" }}\n        >\n          {content}\n        </AbsoluteFill>\n      </AbsoluteFill>\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/long-shadow.tsx"
    }
  ],
  "type": "registry:component"
}
