{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "clipboard",
  "title": "clipboard",
  "description": "Animated clipboard icon",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "icons/clipboard.tsx",
      "content": "'use client';\n\nimport type { Variants } from 'motion/react';\nimport { motion, useAnimation } from 'motion/react';\nimport type { HTMLAttributes } from 'react';\nimport { forwardRef, useId } from 'react';\nimport { useIconAnimation } from '@/lib/use-icon-animation';\nimport { cn } from '@/lib/utils';\n\n/** Imperative controls for the Clipboard icon animation. */\nexport interface ClipboardIconHandle {\n  startAnimation: () => void;\n  stopAnimation: () => void;\n}\n\ninterface ClipboardIconProps extends HTMLAttributes<HTMLDivElement> {\n  size?: number;\n}\n\nconst FRONT_BOARD_PATH =\n  'M17.0235 3.03358L16.0689 2.77924C13.369 2.05986 12.019 1.70018 10.9555 2.31074C9.89196 2.9213 9.53023 4.26367 8.80678 6.94841L7.78366 10.7452C7.0602 13.4299 6.69848 14.7723 7.3125 15.8298C7.92652 16.8874 9.27651 17.247 11.9765 17.9664L12.9311 18.2208C15.631 18.9401 16.981 19.2998 18.0445 18.6893C19.108 18.0787 19.4698 16.7363 20.1932 14.0516L21.2163 10.2548C21.9398 7.57005 22.3015 6.22768 21.6875 5.17016C21.0735 4.11264 19.7235 3.75295 17.0235 3.03358Z';\n\n// The front clipboard merges over the rear board, pauses, then duplicates.\nconst frontBoardVariants: Variants = {\n  normal: {\n    x: 0,\n    y: 0,\n    scale: 1,\n    rotate: 0,\n    transition: { type: 'spring', duration: 0.3, bounce: 0 },\n  },\n  animate: {\n    x: [0, 0.35, -5, -5, 0.45, 0],\n    y: [0, -0.25, 3, 3, -0.3, 0],\n    scale: [1, 1.025, 1, 1, 1.035, 1],\n    rotate: [0, 1.5, -30, -30, 1, 0],\n    transition: {\n      duration: 0.9,\n      ease: 'easeInOut',\n      times: [0, 0.1, 0.38, 0.52, 0.84, 1],\n    },\n  },\n};\n\nconst generatedGeometryVariants: Variants = {\n  normal: { visibility: 'hidden', transition: { duration: 0.08 } },\n  animate: { visibility: 'visible', transition: { duration: 0 } },\n};\n\n/** Animated Clipboard icon with imperative hover controls. */\nconst ClipboardIcon = forwardRef<ClipboardIconHandle, ClipboardIconProps>(\n  ({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {\n    const occlusionMaskId = useId();\n    const controls = useAnimation();\n    const { handleMouseEnter, handleMouseLeave } = useIconAnimation({\n      controls,\n      onMouseEnter,\n      onMouseLeave,\n      ref,\n    });\n\n    return (\n      <div\n        className={cn(className)}\n        onMouseEnter={handleMouseEnter}\n        onMouseLeave={handleMouseLeave}\n        {...props}\n      >\n        <svg\n          xmlns=\"http://www.w3.org/2000/svg\"\n          width={size}\n          height={size}\n          viewBox=\"0 0 24 24\"\n          fill=\"none\"\n          overflow=\"visible\"\n        >\n          <defs>\n            <mask\n              id={occlusionMaskId}\n              maskUnits=\"userSpaceOnUse\"\n              x=\"-4\"\n              y=\"-4\"\n              width=\"32\"\n              height=\"32\"\n            >\n              <rect x=\"-4\" y=\"-4\" width=\"32\" height=\"32\" fill=\"white\" />\n              <motion.g\n                variants={generatedGeometryVariants}\n                animate={controls}\n                initial=\"normal\"\n              >\n                <motion.path\n                  d={FRONT_BOARD_PATH}\n                  fill=\"black\"\n                  stroke=\"black\"\n                  strokeLinejoin=\"round\"\n                  strokeWidth=\"2.5\"\n                  variants={frontBoardVariants}\n                  animate={controls}\n                  initial=\"normal\"\n                  style={{ transformOrigin: '14.5px 10.5px' }}\n                />\n              </motion.g>\n            </mask>\n          </defs>\n          <motion.g\n            variants={frontBoardVariants}\n            animate={controls}\n            initial=\"normal\"\n            style={{ transformOrigin: '14.5px 10.5px' }}\n          >\n            <path\n              d={FRONT_BOARD_PATH}\n              stroke=\"currentColor\"\n              strokeWidth=\"1.5\"\n            />\n            <path\n              d=\"M16.8538 7.43306C16.8538 8.24714 16.1901 8.90709 15.3714 8.90709C14.5527 8.90709 13.889 8.24714 13.889 7.43306C13.889 6.61898 14.5527 5.95904 15.3714 5.95904C16.1901 5.95904 16.8538 6.61898 16.8538 7.43306Z\"\n              stroke=\"currentColor\"\n              strokeWidth=\"1.5\"\n            />\n          </motion.g>\n          <path\n            d=\"M12 20.9463L11.0477 21.2056C8.35403 21.9391 7.00722 22.3059 5.94619 21.6833C4.88517 21.0608 4.52429 19.6921 3.80253 16.9547L2.78182 13.0834C2.06006 10.346 1.69918 8.97731 2.31177 7.89904C2.84167 6.96631 4 7.00027 5.5 7.00015\"\n            stroke=\"currentColor\"\n            strokeLinecap=\"round\"\n            strokeWidth=\"1.5\"\n            mask={`url(#${occlusionMaskId})`}\n          />\n        </svg>\n      </div>\n    );\n  }\n);\n\nClipboardIcon.displayName = 'ClipboardIcon';\n\nexport { ClipboardIcon };\n",
      "type": "registry:ui"
    },
    {
      "path": "lib/use-icon-animation.ts",
      "content": "'use client';\n\nimport type { LegacyAnimationControls } from 'motion/react';\nimport { useReducedMotion } from 'motion/react';\nimport type {\n  ForwardedRef,\n  MouseEventHandler,\n} from 'react';\nimport { useCallback, useImperativeHandle, useRef } from 'react';\n\nexport interface AnimatedIconHandle {\n  startAnimation: () => void;\n  stopAnimation: () => void;\n}\n\ninterface UseIconAnimationOptions {\n  controls: LegacyAnimationControls;\n  loops?: boolean;\n  onMouseEnter?: MouseEventHandler<HTMLDivElement>;\n  onMouseLeave?: MouseEventHandler<HTMLDivElement>;\n  ref: ForwardedRef<AnimatedIconHandle>;\n}\n\nexport function useIconAnimation({\n  controls,\n  loops = false,\n  onMouseEnter,\n  onMouseLeave,\n  ref,\n}: UseIconAnimationOptions) {\n  const shouldReduceMotion = useReducedMotion();\n  const isControlledRef = useRef(false);\n  const isPlayingRef = useRef(false);\n  const runRef = useRef(0);\n\n  const startAnimation = useCallback(() => {\n    if (shouldReduceMotion || isPlayingRef.current) return;\n\n    isPlayingRef.current = true;\n    const run = ++runRef.current;\n    controls.set('normal');\n    void controls.start('animate').then(() => {\n      if (runRef.current === run) isPlayingRef.current = false;\n    });\n  }, [controls, shouldReduceMotion]);\n\n  const stopAnimation = useCallback(() => {\n    if (!loops) return;\n\n    runRef.current++;\n    isPlayingRef.current = false;\n    void controls.start('normal');\n  }, [controls, loops]);\n\n  useImperativeHandle(\n    ref,\n    () => {\n      isControlledRef.current = true;\n      return { startAnimation, stopAnimation };\n    },\n    [startAnimation, stopAnimation]\n  );\n\n  const handleMouseEnter = useCallback<MouseEventHandler<HTMLDivElement>>(\n    (event) => {\n      onMouseEnter?.(event);\n      if (!isControlledRef.current) startAnimation();\n    },\n    [onMouseEnter, startAnimation]\n  );\n\n  const handleMouseLeave = useCallback<MouseEventHandler<HTMLDivElement>>(\n    (event) => {\n      onMouseLeave?.(event);\n      if (!isControlledRef.current) stopAnimation();\n    },\n    [onMouseLeave, stopAnimation]\n  );\n\n  return { handleMouseEnter, handleMouseLeave };\n}\n",
      "type": "registry:lib"
    }
  ],
  "type": "registry:ui"
}