{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "components-community-radial-intro",
  "title": "Radial Intro",
  "description": "A circular intro animation component that arranges elements in a radial layout, smoothly transitioning them into orbit with looping motion.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/components/community/radial-intro/index.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport {\n  LayoutGroup,\n  motion,\n  useAnimate,\n  delay,\n  type Transition,\n  type AnimationSequence,\n} from 'motion/react';\n\ninterface ComponentProps {\n  orbitItems: OrbitItem[];\n  stageSize?: number;\n  imageSize?: number;\n}\n\ntype OrbitItem = {\n  id: number;\n  name: string;\n  src: string;\n};\n\nconst transition: Transition = {\n  delay: 0,\n  stiffness: 300,\n  damping: 35,\n  type: 'spring',\n  restSpeed: 0.01,\n  restDelta: 0.01,\n};\n\nconst spinConfig = {\n  duration: 30,\n  ease: 'linear' as const,\n  repeat: Infinity,\n};\n\nconst qsa = (root: Element, sel: string) =>\n  Array.from(root.querySelectorAll(sel));\n\nconst angleOf = (el: Element) => Number((el as HTMLElement).dataset.angle || 0);\n\nconst armOfImg = (img: Element) =>\n  (img as HTMLElement).closest('[data-arm]') as HTMLElement | null;\n\nfunction RadialIntro({\n  orbitItems,\n  stageSize = 320,\n  imageSize = 60,\n}: ComponentProps) {\n  const step = 360 / orbitItems.length;\n  const [scope, animate] = useAnimate();\n\n  React.useEffect(() => {\n    const root = scope.current;\n    if (!root) return;\n\n    // get arm and image elements\n    const arms = qsa(root, '[data-arm]');\n    const imgs = qsa(root, '[data-arm-image]');\n    const stops: Array<() => void> = [];\n\n    // image lift-in\n    delay(() => animate(imgs, { top: 0 }, transition), 250);\n\n    // build sequence for orbit placement\n    const orbitPlacementSequence: AnimationSequence = [\n      ...arms.map((el): [Element, Record<string, any>, any] => [\n        el,\n        { rotate: angleOf(el) },\n        { ...transition, at: 0 },\n      ]),\n      ...imgs.map((img): [Element, Record<string, any>, any] => [\n        img,\n        { rotate: -angleOf(armOfImg(img)!), opacity: 1 },\n        { ...transition, at: 0 },\n      ]),\n    ];\n\n    // play placement sequence\n    delay(() => animate(orbitPlacementSequence), 700);\n\n    // start continuous spin for arms and images\n    delay(() => {\n      // arms spin clockwise\n      arms.forEach((el) => {\n        const angle = angleOf(el);\n        const ctrl = animate(el, { rotate: [angle, angle + 360] }, spinConfig);\n        stops.push(() => ctrl.cancel());\n      });\n\n      // images counter-spin to stay upright\n      imgs.forEach((img) => {\n        const arm = armOfImg(img);\n        const angle = arm ? angleOf(arm) : 0;\n        const ctrl = animate(\n          img,\n          { rotate: [-angle, -angle - 360] },\n          spinConfig,\n        );\n        stops.push(() => ctrl.cancel());\n      });\n    }, 1300);\n\n    return () => stops.forEach((stop) => stop());\n  }, []);\n\n  return (\n    <LayoutGroup>\n      <motion.div\n        ref={scope}\n        className=\"relative overflow-visible\"\n        style={{ width: stageSize, height: stageSize }}\n        initial={false}\n      >\n        {orbitItems.map((item, i) => (\n          <motion.div\n            key={item.id}\n            data-arm\n            className=\"will-change-transform absolute inset-0\"\n            style={{ zIndex: orbitItems.length - i }}\n            data-angle={i * step}\n            layoutId={`arm-${item.id}`}\n          >\n            <motion.img\n              data-arm-image\n              className=\"rounded-full object-fill absolute left-1/2 top-1/2 aspect-square translate -translate-x-1/2\"\n              style={{\n                width: imageSize,\n                height: imageSize,\n                opacity: i === 0 ? 1 : 0,\n              }}\n              src={item.src}\n              alt={item.name}\n              draggable={false}\n              layoutId={`arm-img-${item.id}`}\n            />\n          </motion.div>\n        ))}\n      </motion.div>\n    </LayoutGroup>\n  );\n}\n\nexport { RadialIntro };\n",
      "type": "registry:ui",
      "target": "components/animate-ui/components/community/radial-intro.tsx"
    }
  ],
  "type": "registry:ui"
}