{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "mail-01",
  "title": "mail-01",
  "description": "Animated mail icon",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "icons/mail-01.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\nexport interface Mail01IconHandle {\n  startAnimation: () => void;\n  stopAnimation: () => void;\n}\n\ninterface Mail01IconProps extends HTMLAttributes<HTMLDivElement> {\n  size?: number;\n}\n\nconst MAIL_CLOSED =\n  'M2 6L8.91302 9.91697C11.4616 11.361 12.5384 11.361 15.087 9.91697L22 6';\nconst MAIL_EDGE_ON =\n  'M2 6L8.91302 6.55C11.4616 6.8 12.5384 6.8 15.087 6.55L22 6';\nconst MAIL_OPEN =\n  'M2 6L8.91302 1.6C11.4616 -0.05 12.5384 -0.05 15.087 1.6L22 6';\nconst MAIL_OPEN_OVERSHOOT =\n  'M2 6L8.91302 1.25C11.4616 -0.45 12.5384 -0.45 15.087 1.25L22 6';\nconst MAIL_CLOSED_OVERSHOOT =\n  'M2 6L8.91302 10.15C11.4616 11.65 12.5384 11.65 15.087 10.15L22 6';\nconst MAIL_REAR_EDGE =\n  'M3.24496 4.79066C4.37608 3.65523 5.95033 3.61568 9.09882 3.53656C11.0393 3.48781 12.9607 3.48781 14.9012 3.53657C18.0497 3.61568 19.6239 3.65523 20.7551 4.79066';\nconst MAIL_ENTRY_LINE =\n  'M5.2 7.4C8.7 9.1 15.3 9.1 18.8 7.4';\n\n// The flap turns edge-on, opens fully, then softly overshoots the closed position.\nconst flapVariants: Variants = {\n  normal: { d: MAIL_CLOSED },\n  animate: {\n    d: [\n      MAIL_CLOSED,\n      MAIL_EDGE_ON,\n      MAIL_OPEN,\n      MAIL_OPEN_OVERSHOOT,\n      MAIL_OPEN,\n      MAIL_OPEN,\n      MAIL_EDGE_ON,\n      MAIL_CLOSED_OVERSHOOT,\n      MAIL_CLOSED,\n    ],\n    transition: {\n      duration: 0.94,\n      ease: [0.77, 0, 0.175, 1],\n      times: [0, 0.13, 0.28, 0.38, 0.5, 0.62, 0.76, 0.9, 1],\n    },\n  },\n};\n\nconst bodyVariants: Variants = {\n  normal: { transform: 'translateY(0px) scaleX(1) scaleY(1)' },\n  animate: {\n    transform: [\n      'translateY(0px) scaleX(1) scaleY(1)',\n      'translateY(0px) scaleX(1) scaleY(1)',\n      'translateY(0.12px) scaleX(1.008) scaleY(0.992)',\n      'translateY(0.45px) scaleX(1.035) scaleY(0.958)',\n      'translateY(-0.32px) scaleX(0.985) scaleY(1.022)',\n      'translateY(0.12px) scaleX(1.008) scaleY(0.994)',\n      'translateY(0px) scaleX(1) scaleY(1)',\n    ],\n    transition: {\n      duration: 0.94,\n      ease: [0.23, 1, 0.32, 1],\n      times: [0, 0.28, 0.38, 0.5, 0.68, 0.84, 1],\n    },\n  },\n};\n\nconst generatedGeometryVariants: Variants = {\n  normal: { visibility: 'hidden' },\n  animate: {\n    visibility: [\n      'hidden',\n      'hidden',\n      'visible',\n      'visible',\n      'hidden',\n      'hidden',\n    ],\n    transition: {\n      duration: 0.94,\n      ease: 'linear',\n      times: [0, 0.26, 0.27, 0.66, 0.67, 1],\n    },\n  },\n};\n\nconst Mail01Icon = forwardRef<Mail01IconHandle, Mail01IconProps>(\n  ({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {\n    const controls = useAnimation();\n    const maskId = `mail-rear-edge-${useId().replaceAll(':', '')}`;\n    const flapMaskId = `mail-flap-${useId().replaceAll(':', '')}`;\n    const { handleMouseEnter, handleMouseLeave } = useIconAnimation({\n      controls,\n      loops: false,\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={maskId}\n              x=\"0\"\n              y=\"0\"\n              width=\"24\"\n              height=\"24\"\n              maskUnits=\"userSpaceOnUse\"\n            >\n              <rect width=\"24\" height=\"24\" fill=\"white\" />\n              <motion.g\n                variants={generatedGeometryVariants}\n                animate={controls}\n                initial=\"normal\"\n              >\n                <path\n                  d={MAIL_REAR_EDGE}\n                  stroke=\"black\"\n                  strokeLinecap=\"round\"\n                  strokeWidth=\"2.2\"\n                />\n              </motion.g>\n            </mask>\n            <mask\n              id={flapMaskId}\n              x=\"0\"\n              y=\"0\"\n              width=\"24\"\n              height=\"24\"\n              maskUnits=\"userSpaceOnUse\"\n            >\n              <rect width=\"24\" height=\"24\" fill=\"white\" />\n              <motion.path\n                d={MAIL_CLOSED}\n                fill=\"black\"\n                variants={flapVariants}\n                animate={controls}\n                initial=\"normal\"\n              />\n            </mask>\n          </defs>\n          <motion.path\n            d=\"M2 6L8.91302 9.91697C11.4616 11.361 12.5384 11.361 15.087 9.91697L22 6\"\n            stroke=\"currentColor\"\n            strokeLinejoin=\"round\"\n            strokeWidth=\"1.5\"\n            variants={flapVariants}\n            animate={controls}\n            initial=\"normal\"\n          />\n          <motion.g\n            variants={bodyVariants}\n            animate={controls}\n            initial=\"normal\"\n            style={{ transformOrigin: '12px 12px' }}\n          >\n            <path\n              d=\"M2.01577 13.4756C2.08114 16.5412 2.11383 18.0739 3.24496 19.2094C4.37608 20.3448 5.95033 20.3843 9.09883 20.4634C11.0393 20.5122 12.9607 20.5122 14.9012 20.4634C18.0497 20.3843 19.6239 20.3448 20.7551 19.2094C21.8862 18.0739 21.9189 16.5412 21.9842 13.4756C22.0053 12.4899 22.0053 11.5101 21.9842 10.5244C21.9189 7.45886 21.8862 5.92609 20.7551 4.79066C19.6239 3.65523 18.0497 3.61568 14.9012 3.53657C12.9607 3.48781 11.0393 3.48781 9.09882 3.53656C5.95033 3.61566 4.37608 3.65521 3.24495 4.79065C2.11382 5.92608 2.08114 7.45885 2.01576 10.5244C1.99474 11.5101 1.99475 12.4899 2.01577 13.4756Z\"\n              stroke=\"currentColor\"\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n              strokeWidth=\"1.5\"\n              mask={`url(#${maskId})`}\n            />\n            <path\n              d={MAIL_ENTRY_LINE}\n              stroke=\"currentColor\"\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n              strokeWidth=\"1.5\"\n              mask={`url(#${flapMaskId})`}\n            />\n          </motion.g>\n        </svg>\n      </div>\n    );\n  }\n);\n\nMail01Icon.displayName = 'Mail01Icon';\n\nexport { Mail01Icon };\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"
}