{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "primitives-effects-shine",
  "title": "Shine",
  "description": "An animated light sweep effect with configurable timing, colors, and triggers (hover, tap, or continuous).",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "registry/primitives/effects/shine/index.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { motion } from \"motion/react\";\n\ntype ShineProps = React.ComponentProps<\"div\"> & {\n  color?: string;\n  opacity?: number;\n  delay?: number;\n  duration?: number;\n  loop?: boolean;\n  loopDelay?: number;\n  deg?: number;\n  enable?: boolean;\n  enableOnHover?: boolean;\n  enableOnTap?: boolean;\n  asChild?: boolean;\n  children?: React.ReactNode;\n};\n\nconst Shine = ({\n  color = \"currentColor\",\n  opacity = 0.3,\n  delay = 0,\n  duration = 1200,\n  loop = false,\n  loopDelay = 0,\n  deg = -15,\n  enable = true,\n  enableOnHover = false,\n  enableOnTap = false,\n  asChild = false,\n  style,\n  children,\n  onMouseEnter,\n  onMouseLeave,\n  onPointerDown,\n  ...props\n}: ShineProps) => {\n  const isAlwaysOn = enable && !enableOnHover && !enableOnTap;\n  const [animateState, setAnimateState] = React.useState<\"initial\" | \"shine\">(\n    isAlwaysOn ? \"shine\" : \"initial\",\n  );\n  const hoverLoopTimeoutRef = React.useRef<number | undefined>(undefined);\n  const hoverLoopRafRef = React.useRef<number | undefined>(undefined);\n  const [isHovered, setIsHovered] = React.useState(false);\n  const [currentDelay, setCurrentDelay] = React.useState(delay);\n\n  React.useEffect(() => {\n    setAnimateState(isAlwaysOn ? \"shine\" : \"initial\");\n    if (isAlwaysOn) {\n      setCurrentDelay(delay);\n    }\n  }, [isAlwaysOn, delay]);\n\n  React.useEffect(() => {\n    return () => {\n      if (hoverLoopTimeoutRef.current !== undefined) {\n        window.clearTimeout(hoverLoopTimeoutRef.current);\n      }\n      if (hoverLoopRafRef.current !== undefined) {\n        window.cancelAnimationFrame(hoverLoopRafRef.current);\n      }\n    };\n  }, []);\n\n  const handlePointerDown = React.useCallback(\n    (e: React.PointerEvent<HTMLDivElement>) => {\n      onPointerDown?.(e);\n      if (!enable || !enableOnTap || isAlwaysOn) return;\n      setCurrentDelay(delay);\n      setAnimateState(\"shine\");\n    },\n    [enable, enableOnTap, isAlwaysOn, delay, onPointerDown],\n  );\n\n  const handleMouseEnter = React.useCallback(\n    (e: React.MouseEvent<HTMLDivElement>) => {\n      onMouseEnter?.(e);\n      if (!enable || !enableOnHover || isAlwaysOn) return;\n      setIsHovered(true);\n      setCurrentDelay(delay);\n      setAnimateState(\"shine\");\n    },\n    [enable, enableOnHover, isAlwaysOn, delay, onMouseEnter],\n  );\n\n  const handleMouseLeave = React.useCallback(\n    (e: React.MouseEvent<HTMLDivElement>) => {\n      onMouseLeave?.(e);\n      if (!enable || !enableOnHover || isAlwaysOn) return;\n      setIsHovered(false);\n      if (hoverLoopTimeoutRef.current !== undefined) {\n        window.clearTimeout(hoverLoopTimeoutRef.current);\n        hoverLoopTimeoutRef.current = undefined;\n      }\n      if (hoverLoopRafRef.current !== undefined) {\n        window.cancelAnimationFrame(hoverLoopRafRef.current);\n        hoverLoopRafRef.current = undefined;\n      }\n    },\n    [enable, enableOnHover, isAlwaysOn, onMouseLeave],\n  );\n\n  const scheduleNextShine = React.useCallback((delayMs: number) => {\n    if (hoverLoopTimeoutRef.current !== undefined) {\n      window.clearTimeout(hoverLoopTimeoutRef.current);\n      hoverLoopTimeoutRef.current = undefined;\n    }\n    if (hoverLoopRafRef.current !== undefined) {\n      window.cancelAnimationFrame(hoverLoopRafRef.current);\n      hoverLoopRafRef.current = undefined;\n    }\n    if (delayMs > 0) {\n      hoverLoopTimeoutRef.current = window.setTimeout(() => {\n        setAnimateState(\"shine\");\n        hoverLoopTimeoutRef.current = undefined;\n      }, delayMs);\n    } else {\n      hoverLoopRafRef.current = window.requestAnimationFrame(() => {\n        hoverLoopRafRef.current = window.requestAnimationFrame(() => {\n          setAnimateState(\"shine\");\n          hoverLoopRafRef.current = undefined;\n        });\n      });\n    }\n  }, []);\n\n  const handleAnimationComplete = React.useCallback(() => {\n    if (animateState !== \"shine\") return;\n    if (isAlwaysOn) {\n      if (loop) {\n        setAnimateState(\"initial\");\n        setCurrentDelay(0);\n        scheduleNextShine(loopDelay);\n      }\n      return;\n    }\n\n    if (enableOnHover) {\n      if (isHovered) {\n        if (loop) {\n          setAnimateState(\"initial\");\n          setCurrentDelay(0);\n          scheduleNextShine(loopDelay);\n        } else {\n          setAnimateState(\"initial\");\n        }\n      } else {\n        setAnimateState(\"initial\");\n      }\n      return;\n    }\n\n    if (enableOnTap) {\n      setAnimateState(\"initial\");\n    }\n  }, [\n    animateState,\n    isAlwaysOn,\n    loop,\n    enableOnHover,\n    isHovered,\n    enableOnTap,\n    scheduleNextShine,\n    loopDelay,\n  ]);\n\n  const overlayElement = (\n    <motion.div\n      initial=\"initial\"\n      animate={animateState}\n      variants={{\n        initial: { x: \"-100%\", skewX: deg, transition: { duration: 0 } },\n        shine: { x: \"100%\", skewX: deg },\n      }}\n      transition={{\n        duration: duration / 1000,\n        ease: [0.4, 0, 0.2, 1],\n        delay: currentDelay / 1000,\n      }}\n      style={{\n        position: \"absolute\",\n        inset: 0,\n        zIndex: 10,\n        pointerEvents: \"none\",\n        width: \"100%\",\n        height: \"100%\",\n        willChange: \"transform, opacity\",\n        background: `linear-gradient(to right, transparent, ${color}, transparent)`,\n        opacity,\n        ...style,\n      }}\n      onAnimationComplete={handleAnimationComplete}\n    />\n  );\n\n  if (asChild) {\n    if (!React.isValidElement(children)) {\n      return null;\n    }\n\n    const child = children as React.ReactElement<Record<string, unknown>>;\n    const childProps = (child.props ?? {}) as Record<string, unknown> & {\n      className?: string;\n      style?: React.CSSProperties;\n      onMouseEnter?: (e: React.MouseEvent) => void;\n      onMouseLeave?: (e: React.MouseEvent) => void;\n      onPointerDown?: (e: React.PointerEvent) => void;\n      children?: React.ReactNode;\n    };\n\n    const mergedClassName = [\n      childProps.className,\n      (props as { className?: string }).className,\n    ]\n      .filter(Boolean)\n      .join(\" \");\n    const mergedStyle = {\n      ...(childProps.style || {}),\n      ...(style || {}),\n      position: \"relative\",\n      overflow: \"hidden\",\n    } as React.CSSProperties;\n\n    const onMouseEnter = (e: React.MouseEvent) => {\n      if (typeof childProps.onMouseEnter === \"function\")\n        childProps.onMouseEnter(e);\n      handleMouseEnter(e as React.MouseEvent<HTMLDivElement>);\n    };\n    const onMouseLeave = (e: React.MouseEvent) => {\n      if (typeof childProps.onMouseLeave === \"function\")\n        childProps.onMouseLeave(e);\n      handleMouseLeave(e as React.MouseEvent<HTMLDivElement>);\n    };\n    const onPointerDown = (e: React.PointerEvent) => {\n      if (typeof childProps.onPointerDown === \"function\")\n        childProps.onPointerDown(e);\n      handlePointerDown(e as React.PointerEvent<HTMLDivElement>);\n    };\n\n    const newChildren = (\n      <>\n        {childProps.children}\n        {enable && overlayElement}\n      </>\n    );\n\n    return React.cloneElement(child, {\n      ...props,\n      className: mergedClassName,\n      style: mergedStyle,\n      onMouseEnter,\n      onMouseLeave,\n      onPointerDown,\n      children: newChildren,\n    });\n  }\n\n  return (\n    <div\n      style={{ position: \"relative\", overflow: \"hidden\", ...style }}\n      {...props}\n      onMouseEnter={handleMouseEnter}\n      onMouseLeave={handleMouseLeave}\n      onPointerDown={handlePointerDown}\n    >\n      {children}\n      {enable && overlayElement}\n    </div>\n  );\n};\n\nexport { Shine, type ShineProps };\n",
      "type": "registry:ui",
      "target": "components/unlumen-ui/primitives/effects/shine.tsx"
    }
  ],
  "type": "registry:ui"
}