{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "mail-open",
  "title": "mail-open",
  "description": "Animated mail open icon",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "icons/mail-open.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 MailOpenIconHandle {\n  startAnimation: () => void;\n  stopAnimation: () => void;\n}\n\ninterface MailOpenIconProps extends HTMLAttributes<HTMLDivElement> {\n  size?: number;\n}\n\nconst MAIL_LETTER =\n  'M4.99998 12V6C4.99998 4.11438 4.99998 3.17157 5.58577 2.58579C6.17156 2 7.11437 2 8.99998 2H15C16.8856 2 17.8284 2 18.4142 2.58579C19 3.17157 19 4.11438 19 6V12';\nconst MAIL_LETTER_CLOSED =\n  'M4.99998 12V6C4.99998 4.11438 4.99998 3.17157 5.58577 2.58579C6.17156 2 7.11437 2 8.99998 2H15C16.8856 2 17.8284 2 18.4142 2.58579C19 3.17157 19 4.11438 19 6V12H4.99998Z';\nconst MAIL_TEXT = 'M10 10H14M10 6H14';\n\nconst MAIL_DURATION = 0.92;\nconst MAIL_TIMES = [0, 0.08, 0.16, 0.26, 0.38, 0.5, 0.62, 0.72, 0.82, 0.9, 0.96, 1];\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.006) scaleY(0.994)',\n      'translateY(0.28px) scaleX(1.018) scaleY(0.982)',\n      'translateY(0.5px) scaleX(1.035) scaleY(0.958)',\n      'translateY(0.2px) scaleX(1.014) scaleY(0.986)',\n      'translateY(-0.36px) scaleX(0.985) scaleY(1.022)',\n      'translateY(0.14px) scaleX(1.008) scaleY(0.994)',\n      'translateY(0px) scaleX(1) scaleY(1)',\n      'translateY(0px) scaleX(1) scaleY(1)',\n      'translateY(0px) scaleX(1) scaleY(1)',\n      'translateY(0px) scaleX(1) scaleY(1)',\n    ],\n    transition: {\n      duration: MAIL_DURATION,\n      ease: 'linear',\n      times: MAIL_TIMES,\n    },\n  },\n};\n\n// The current letter keeps its full visibility as it clears the top edge.\nconst outgoingLetterVariants: Variants = {\n  normal: {\n    transform: 'translateY(0px)',\n    transition: { duration: 0.18, ease: [0.23, 1, 0.32, 1] },\n  },\n  animate: {\n    transform: [\n      'translateY(0px)',\n      'translateY(-0.8px)',\n      'translateY(-2.4px)',\n      'translateY(-5.5px)',\n      'translateY(-9.5px)',\n      'translateY(-14px)',\n      'translateY(-18px)',\n      'translateY(-18px)',\n      'translateY(-18px)',\n      'translateY(-18px)',\n      'translateY(-18px)',\n      'translateY(-18px)',\n    ],\n    transition: {\n      duration: MAIL_DURATION,\n      ease: [0.77, 0, 0.175, 1],\n      times: MAIL_TIMES,\n    },\n  },\n};\n\n// A replacement follows from below, passes behind the envelope lip, and settles.\nconst incomingLetterVariants: Variants = {\n  normal: {\n    transform: 'translateY(13px)',\n    transition: { duration: 0.18, ease: [0.23, 1, 0.32, 1] },\n  },\n  animate: {\n    transform: [\n      'translateY(13px)',\n      'translateY(12px)',\n      'translateY(10px)',\n      'translateY(7px)',\n      'translateY(3.5px)',\n      'translateY(0.5px)',\n      'translateY(-0.3px)',\n      'translateY(-0.35px)',\n      'translateY(0.15px)',\n      'translateY(0px)',\n      'translateY(0px)',\n      'translateY(0px)',\n    ],\n    transition: {\n      duration: MAIL_DURATION,\n      ease: [0.77, 0, 0.175, 1],\n      times: MAIL_TIMES,\n    },\n  },\n};\n\nconst generatedGeometryVariants: Variants = {\n  normal: { visibility: 'hidden', transition: { duration: 0, delay: 0.18 } },\n  animate: { visibility: 'visible', transition: { duration: 0 } },\n};\n\n// Once the source clears the lip, it hands off to a complete document outline.\nconst outgoingSourceVariants: Variants = {\n  normal: { visibility: 'visible', transition: { duration: 0, delay: 0.18 } },\n  animate: { visibility: 'hidden', transition: { duration: 0, delay: 0.15 } },\n};\n\nconst outgoingClosedVariants: Variants = {\n  normal: { visibility: 'hidden', transition: { duration: 0 } },\n  animate: { visibility: 'visible', transition: { duration: 0, delay: 0.15 } },\n};\n\nconst MailOpenIcon = forwardRef<MailOpenIconHandle, MailOpenIconProps>(\n  ({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {\n    const controls = useAnimation();\n    const letterWindowId = useId();\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=\"hidden\"\n        >\n          <defs>\n            <clipPath id={letterWindowId} clipPathUnits=\"userSpaceOnUse\">\n              <path d=\"M0 0H24V10L12 15.5014L0 10V0Z\" />\n            </clipPath>\n          </defs>\n          <motion.path\n            d=\"M5.00035 7L3.78154 7.81253C2.90783 8.39501 2.47097 8.68625 2.23422 9.13041C1.99747 9.57457 1.99923 10.0966 2.00273 11.1406C2.00696 12.3975 2.01864 13.6782 2.05099 14.9741C2.12773 18.0487 2.16611 19.586 3.29651 20.7164C4.42691 21.8469 5.98497 21.8858 9.10108 21.9637C11.0397 22.0121 12.9611 22.0121 14.8996 21.9637C18.0158 21.8858 19.5738 21.8469 20.7042 20.7164C21.8346 19.586 21.873 18.0487 21.9497 14.9741C21.9821 13.6782 21.9937 12.3975 21.998 11.1406C22.0015 10.0966 22.0032 9.57456 21.7665 9.13041C21.5297 8.68625 21.0929 8.39501 20.2191 7.81253L19.0003 7\"\n            stroke=\"currentColor\"\n            strokeLinejoin=\"round\"\n            strokeWidth=\"1.5\"\n            variants={bodyVariants}\n            animate={controls}\n            initial=\"normal\"\n            style={{ transformOrigin: '12px 15px' }}\n          />\n          <motion.g\n            clipPath={`url(#${letterWindowId})`}\n            variants={generatedGeometryVariants}\n            animate={controls}\n            initial=\"normal\"\n          >\n            <motion.g\n              variants={incomingLetterVariants}\n              animate={controls}\n              initial=\"normal\"\n            >\n              <path d={MAIL_LETTER} stroke=\"currentColor\" strokeWidth=\"1.5\" />\n              <path\n                d={MAIL_TEXT}\n                stroke=\"currentColor\"\n                strokeLinecap=\"round\"\n                strokeLinejoin=\"round\"\n                strokeWidth=\"1.5\"\n              />\n            </motion.g>\n          </motion.g>\n          <motion.g\n            clipPath={`url(#${letterWindowId})`}\n            variants={outgoingLetterVariants}\n            animate={controls}\n            initial=\"normal\"\n          >\n            <motion.g\n              variants={outgoingSourceVariants}\n              animate={controls}\n              initial=\"normal\"\n            >\n              <path d={MAIL_LETTER} stroke=\"currentColor\" strokeWidth=\"1.5\" />\n              <path\n                d={MAIL_TEXT}\n                stroke=\"currentColor\"\n                strokeLinecap=\"round\"\n                strokeLinejoin=\"round\"\n                strokeWidth=\"1.5\"\n              />\n            </motion.g>\n            <motion.g\n              variants={generatedGeometryVariants}\n              animate={controls}\n              initial=\"normal\"\n            >\n              <motion.g\n                variants={outgoingClosedVariants}\n                animate={controls}\n                initial=\"normal\"\n              >\n                <path\n                  d={MAIL_LETTER_CLOSED}\n                  stroke=\"currentColor\"\n                  strokeLinejoin=\"round\"\n                  strokeWidth=\"1.5\"\n                />\n                <path\n                  d={MAIL_TEXT}\n                  stroke=\"currentColor\"\n                  strokeLinecap=\"round\"\n                  strokeLinejoin=\"round\"\n                  strokeWidth=\"1.5\"\n                />\n              </motion.g>\n            </motion.g>\n          </motion.g>\n          <motion.path\n            d=\"M2 10L8.91302 14.1478C10.417 15.0502 11.169 15.5014 12 15.5014C12.831 15.5014 13.583 15.0502 15.087 14.1478L22 10\"\n            stroke=\"currentColor\"\n            strokeLinejoin=\"round\"\n            strokeWidth=\"1.5\"\n            variants={bodyVariants}\n            animate={controls}\n            initial=\"normal\"\n            style={{ transformOrigin: '12px 15px' }}\n          />\n        </svg>\n      </div>\n    );\n  }\n);\n\nMailOpenIcon.displayName = 'MailOpenIcon';\n\nexport { MailOpenIcon };\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"
}