{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "icon",
  "type": "registry:ui",
  "title": "Icon",
  "description": "Base component to use animated icons.",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "registry/icons/icon/index.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport {\n  SVGMotionProps,\n  useAnimation,\n  type LegacyAnimationControls,\n  type Variants,\n} from 'motion/react';\n\nimport { cn } from '@/lib/utils';\n\nconst staticAnimations = {\n  path: {\n    initial: { pathLength: 1, opacity: 1 },\n    animate: {\n      pathLength: [0.05, 1],\n      opacity: [0, 1],\n      transition: {\n        duration: 0.8,\n        ease: 'easeInOut',\n        opacity: { duration: 0.01 },\n      },\n    },\n  } as Variants,\n  'path-loop': {\n    initial: { pathLength: 1, opacity: 1 },\n    animate: {\n      pathLength: [1, 0.05, 1],\n      opacity: [1, 0, 1],\n      transition: {\n        duration: 1.6,\n        ease: 'easeInOut',\n        opacity: { duration: 0.01 },\n      },\n    },\n  } as Variants,\n} as const;\n\ntype StaticAnimations = keyof typeof staticAnimations;\ntype TriggerProp<T = string> = boolean | StaticAnimations | T;\n\ninterface AnimateIconContextValue {\n  controls: LegacyAnimationControls | undefined;\n  animation: StaticAnimations | string;\n  loop: boolean;\n  loopDelay: number;\n}\n\ninterface DefaultIconProps<T = string> {\n  animate?: TriggerProp<T>;\n  onAnimateChange?: (\n    value: boolean,\n    animation: StaticAnimations | string,\n  ) => void;\n  animateOnHover?: TriggerProp<T>;\n  animateOnTap?: TriggerProp<T>;\n  animation?: T | StaticAnimations;\n  loop?: boolean;\n  loopDelay?: number;\n  onAnimateStart?: () => void;\n  onAnimateEnd?: () => void;\n}\n\ninterface AnimateIconProps<T = string> extends DefaultIconProps<T> {\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  children: React.ReactElement<any, any>;\n}\n\ninterface IconProps<T>\n  extends DefaultIconProps<T>,\n    Omit<\n      SVGMotionProps<SVGSVGElement>,\n      'animate' | 'onAnimationStart' | 'onAnimationEnd'\n    > {\n  size?: number;\n}\n\ninterface IconWrapperProps<T> extends IconProps<T> {\n  icon: React.ComponentType<IconProps<T>>;\n}\n\nconst AnimateIconContext = React.createContext<AnimateIconContextValue | null>(\n  null,\n);\n\nfunction useAnimateIconContext() {\n  const context = React.useContext(AnimateIconContext);\n  if (!context)\n    return {\n      controls: undefined,\n      animation: 'default',\n      loop: false,\n      loopDelay: 0,\n    };\n  return context;\n}\n\nfunction AnimateIcon({\n  animate,\n  onAnimateChange,\n  animateOnHover,\n  animateOnTap,\n  animation = 'default',\n  loop = false,\n  loopDelay = 0,\n  onAnimateStart,\n  onAnimateEnd,\n  children,\n}: AnimateIconProps) {\n  const controls = useAnimation();\n  const [localAnimate, setLocalAnimate] = React.useState(!!animate);\n  const currentAnimation = React.useRef(animation);\n\n  const startAnimation = React.useCallback(\n    (trigger: TriggerProp) => {\n      currentAnimation.current =\n        typeof trigger === 'string' ? trigger : animation;\n      setLocalAnimate(true);\n    },\n    [animation],\n  );\n\n  const stopAnimation = React.useCallback(() => {\n    setLocalAnimate(false);\n  }, []);\n\n  React.useEffect(() => {\n    currentAnimation.current =\n      typeof animate === 'string' ? animate : animation;\n    setLocalAnimate(!!animate);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [animate]);\n\n  React.useEffect(\n    () => onAnimateChange?.(localAnimate, currentAnimation.current),\n    [localAnimate, onAnimateChange],\n  );\n\n  React.useEffect(() => {\n    if (localAnimate) onAnimateStart?.();\n    controls.start(localAnimate ? 'animate' : 'initial').then(() => {\n      if (localAnimate) onAnimateEnd?.();\n    });\n  }, [localAnimate, controls, onAnimateStart, onAnimateEnd]);\n\n  const handleMouseEnter = (e: MouseEvent) => {\n    if (animateOnHover) startAnimation(animateOnHover);\n    children.props?.onMouseEnter?.(e);\n  };\n  const handleMouseLeave = (e: MouseEvent) => {\n    if (animateOnHover || animateOnTap) stopAnimation();\n    children.props?.onMouseLeave?.(e);\n  };\n  const handlePointerDown = (e: PointerEvent) => {\n    if (animateOnTap) startAnimation(animateOnTap);\n    children.props?.onPointerDown?.(e);\n  };\n  const handlePointerUp = (e: PointerEvent) => {\n    if (animateOnTap) stopAnimation();\n    children.props?.onPointerUp?.(e);\n  };\n\n  const child = React.Children.only(children);\n  const cloned = React.cloneElement(child, {\n    onMouseEnter: handleMouseEnter,\n    onMouseLeave: handleMouseLeave,\n    onPointerDown: handlePointerDown,\n    onPointerUp: handlePointerUp,\n  });\n\n  return (\n    <AnimateIconContext.Provider\n      value={{\n        controls,\n        animation: currentAnimation.current,\n        loop,\n        loopDelay,\n      }}\n    >\n      {cloned}\n    </AnimateIconContext.Provider>\n  );\n}\n\nconst pathClassName =\n  \"[&_[stroke-dasharray='1px_1px']]:![stroke-dasharray:1px_0px]\";\n\nfunction IconWrapper<T extends string>({\n  size = 28,\n  animation: animationProp,\n  animate,\n  onAnimateChange,\n  animateOnHover = false,\n  animateOnTap = false,\n  icon: IconComponent,\n  loop = false,\n  loopDelay = 0,\n  onAnimateStart,\n  onAnimateEnd,\n  className,\n  ...props\n}: IconWrapperProps<T>) {\n  const context = React.useContext(AnimateIconContext);\n\n  if (context) {\n    const {\n      controls,\n      animation: parentAnimation,\n      loop: parentLoop,\n      loopDelay: parentLoopDelay,\n    } = context;\n    const animationToUse = animationProp ?? parentAnimation;\n    const loopToUse = loop || parentLoop;\n    const loopDelayToUse = loopDelay || parentLoopDelay;\n\n    return (\n      <AnimateIconContext.Provider\n        value={{\n          controls,\n          animation: animationToUse,\n          loop: loopToUse,\n          loopDelay: loopDelayToUse,\n        }}\n      >\n        <IconComponent\n          size={size}\n          className={cn(\n            className,\n            (animationToUse === 'path' || animationToUse === 'path-loop') &&\n              pathClassName,\n          )}\n          {...props}\n        />\n      </AnimateIconContext.Provider>\n    );\n  }\n\n  if (\n    animate !== undefined ||\n    onAnimateChange !== undefined ||\n    animateOnHover ||\n    animateOnTap ||\n    animationProp\n  ) {\n    return (\n      <AnimateIcon\n        animate={animate}\n        onAnimateChange={onAnimateChange}\n        animateOnHover={animateOnHover}\n        animateOnTap={animateOnTap}\n        animation={animationProp}\n        loop={loop}\n        loopDelay={loopDelay}\n        onAnimateStart={onAnimateStart}\n        onAnimateEnd={onAnimateEnd}\n      >\n        <IconComponent\n          size={size}\n          className={cn(\n            className,\n            (animationProp === 'path' || animationProp === 'path-loop') &&\n              pathClassName,\n          )}\n          {...props}\n        />\n      </AnimateIcon>\n    );\n  }\n\n  return (\n    <IconComponent\n      size={size}\n      className={cn(\n        className,\n        (animationProp === 'path' || animationProp === 'path-loop') &&\n          pathClassName,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction getVariants<\n  V extends { default: T; [key: string]: T },\n  T extends Record<string, Variants>,\n>(animations: V): T {\n  // eslint-disable-next-line react-hooks/rules-of-hooks\n  const { animation: animationType, loop, loopDelay } = useAnimateIconContext();\n\n  let result: T;\n\n  if (animationType in staticAnimations) {\n    const variant = staticAnimations[animationType as StaticAnimations];\n    result = {} as T;\n    for (const key in animations.default) {\n      if (\n        (animationType === 'path' || animationType === 'path-loop') &&\n        key.includes('group')\n      )\n        continue;\n      result[key] = variant as T[Extract<keyof T, string>];\n    }\n  } else {\n    result = (animations[animationType as keyof V] as T) ?? animations.default;\n  }\n\n  if (loop) {\n    for (const key in result) {\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n      const state = result[key] as any;\n      const transition = state.animate?.transition;\n      if (!transition) continue;\n\n      const hasNestedKeys = Object.values(transition).some(\n        (v) =>\n          typeof v === 'object' &&\n          v !== null &&\n          ('ease' in v || 'duration' in v || 'times' in v),\n      );\n\n      if (hasNestedKeys) {\n        for (const prop in transition) {\n          const subTrans = transition[prop];\n          if (typeof subTrans === 'object' && subTrans !== null) {\n            transition[prop] = {\n              ...subTrans,\n              repeat: Infinity,\n              repeatType: 'loop',\n              repeatDelay: loopDelay,\n            };\n          }\n        }\n      } else {\n        state.animate.transition = {\n          ...transition,\n          repeat: Infinity,\n          repeatType: 'loop',\n          repeatDelay: loopDelay,\n        };\n      }\n    }\n  }\n\n  return result;\n}\n\nexport {\n  pathClassName,\n  staticAnimations,\n  AnimateIcon,\n  IconWrapper,\n  useAnimateIconContext,\n  getVariants,\n  type IconProps,\n  type IconWrapperProps,\n  type AnimateIconProps,\n  type AnimateIconContextValue,\n};\n",
      "type": "registry:ui",
      "target": "components/animate-ui/icons/icon.tsx"
    }
  ]
}