{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "maximize-screen",
  "title": "maximize-screen",
  "description": "Animated maximize screen icon",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "icons/maximize-screen.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 MaximizeScreenIconHandle {\n  startAnimation: () => void;\n  stopAnimation: () => void;\n}\n\ninterface MaximizeScreenIconProps extends HTMLAttributes<HTMLDivElement> {\n  size?: number;\n}\n\nconst MAXIMIZE_WINDOW =\n  'M2 17C2 15.1144 2 14.1716 2.58579 13.5858C3.17157 13 4.11438 13 6 13H7C8.88562 13 9.82843 13 10.4142 13.5858C11 14.1716 11 15.1144 11 17V18C11 19.8856 11 20.8284 10.4142 21.4142C9.82843 22 8.88562 22 7 22H6C4.11438 22 3.17157 22 2.58579 21.4142C2 20.8284 2 19.8856 2 18V17Z';\nconst MAXIMIZE_OUTER_SOURCE =\n  'M2 8.5V10.5M14 2H10M22 14V10M13.5 22H15.5M2.05986 5.5C2.21387 4.43442 2.51347 3.67903 3.09625 3.09625C3.67903 2.51347 4.43442 2.21387 5.5 2.05986M18.5 2.05986C19.5656 2.21387 20.321 2.51347 20.9037 3.09625C21.4865 3.67903 21.7861 4.43442 21.9401 5.5M21.9401 18.5C21.7861 19.5656 21.4865 20.321 20.9037 20.9037C20.321 21.4865 19.5656 21.7861 18.5 21.9401';\n\nconst MAXIMIZE_DURATION = 0.86;\nconst MAXIMIZE_TIMES = [0, 0.07, 0.14, 0.23, 0.34, 0.46, 0.58, 0.68, 0.76, 0.82, 0.88, 0.94, 1];\n\nfunction createExpandedWindowPath({\n  right,\n  top,\n}: {\n  right: number;\n  top: number;\n}) {\n  return `M2 ${top + 4}C2 ${top + 2.1144} 2 ${top + 1.1716} 2.58579 ${top + 0.5858}C3.17157 ${top} 4.11438 ${top} 6 ${top}H${right - 4}C${right - 2.11438} ${top} ${right - 1.17157} ${top} ${right - 0.5858} ${top + 0.5858}C${right} ${top + 1.1716} ${right} ${top + 2.1144} ${right} ${top + 4}V18C${right} 19.8856 ${right} 20.8284 ${right - 0.5858} 21.4142C${right - 1.17157} 22 ${right - 2.11438} 22 ${right - 4} 22H6C4.11438 22 3.17157 22 2.58579 21.4142C2 20.8284 2 19.8856 2 18V${top + 4}Z`;\n}\n\nconst EXPANDING_WINDOW_FRAMES = [\n  MAXIMIZE_WINDOW,\n  MAXIMIZE_WINDOW,\n  createExpandedWindowPath({ right: 11.66, top: 12.34 }),\n  createExpandedWindowPath({ right: 12.98, top: 11.02 }),\n  createExpandedWindowPath({ right: 15.18, top: 8.82 }),\n  createExpandedWindowPath({ right: 17.82, top: 6.18 }),\n  createExpandedWindowPath({ right: 20.02, top: 3.98 }),\n  createExpandedWindowPath({ right: 21.45, top: 2.55 }),\n  createExpandedWindowPath({ right: 22, top: 2 }),\n  createExpandedWindowPath({ right: 22, top: 2 }),\n  createExpandedWindowPath({ right: 20.02, top: 3.98 }),\n  createExpandedWindowPath({ right: 15.18, top: 8.82 }),\n  MAXIMIZE_WINDOW,\n];\n\n// The arrow leads the original window into the outer frame.\nconst arrowVariants: Variants = {\n  normal: { transform: 'translate(0px, 0px)' },\n  animate: {\n    transform: [\n      'translate(0px, 0px)',\n      'translate(0.2px, -0.2px)',\n      'translate(0.55px, -0.55px)',\n      'translate(1.1px, -1.1px)',\n      'translate(1.45px, -1.45px)',\n      'translate(0.9px, -0.9px)',\n      'translate(0.35px, -0.35px)',\n      'translate(0px, 0px)',\n      'translate(0px, 0px)',\n      'translate(0px, 0px)',\n      'translate(0.35px, -0.35px)',\n      'translate(0.15px, -0.15px)',\n      'translate(0px, 0px)',\n    ],\n    transition: {\n      duration: MAXIMIZE_DURATION,\n      ease: [0.77, 0, 0.175, 1],\n      times: MAXIMIZE_TIMES,\n    },\n  },\n};\n\nconst sourceWindowVariants: Variants = {\n  normal: {\n    d: MAXIMIZE_WINDOW,\n    strokeDasharray: '100 0',\n    strokeDashoffset: 0,\n    visibility: 'visible',\n  },\n  animate: {\n    d: EXPANDING_WINDOW_FRAMES,\n    strokeDasharray: [\n      '100 0',\n      '100 0',\n      '100 0',\n      '100 0',\n      '100 0',\n      '100 0',\n      '36 0.4',\n      '12 1',\n      '5 1.8',\n      '2.6 2.2',\n      '5 1.8',\n      '36 0.4',\n      '100 0',\n    ],\n    strokeDashoffset: [0, 0, 0, 0, 0, 0, 0.5, 1.5, 3, 4, 3, 0.5, 0],\n    visibility: [\n      'visible',\n      'visible',\n      'visible',\n      'visible',\n      'visible',\n      'visible',\n      'visible',\n      'visible',\n      'visible',\n      'visible',\n      'hidden',\n      'hidden',\n      'visible',\n    ],\n    transition: {\n      duration: MAXIMIZE_DURATION,\n      ease: 'linear',\n      times: MAXIMIZE_TIMES,\n    },\n  },\n};\n\nconst sourceOuterFrameVariants: Variants = {\n  normal: { pathLength: 1 },\n  animate: {\n    pathLength: [1, 1, 1, 0.92, 0.72, 0.42, 0.15, 0, 0, 0, 0.18, 0.55, 1],\n    transition: {\n      duration: MAXIMIZE_DURATION,\n      ease: 'linear',\n      times: MAXIMIZE_TIMES,\n    },\n  },\n};\n\nconst arrivingWindowVariants: Variants = {\n  normal: { transform: 'translate(-12px, 12px)' },\n  animate: {\n    transform: [\n      'translate(-12px, 12px)',\n      'translate(-12px, 12px)',\n      'translate(-12px, 12px)',\n      'translate(-12px, 12px)',\n      'translate(-12px, 12px)',\n      'translate(-9px, 9px)',\n      'translate(-4.5px, 4.5px)',\n      'translate(-1px, 1px)',\n      'translate(0px, 0px)',\n      'translate(0px, 0px)',\n      'translate(0px, 0px)',\n      'translate(0px, 0px)',\n      'translate(0px, 0px)',\n    ],\n    transition: {\n      duration: MAXIMIZE_DURATION,\n      ease: 'linear',\n      times: MAXIMIZE_TIMES,\n    },\n  },\n};\n\nconst generatedGeometryVariants: Variants = {\n  normal: { visibility: 'hidden', transition: { duration: 0 } },\n  animate: {\n    visibility: ['hidden', 'hidden', 'hidden', 'hidden', 'visible', 'visible', 'visible', 'visible', 'visible', 'visible', 'visible', 'visible', 'hidden'],\n    transition: {\n      duration: MAXIMIZE_DURATION,\n      ease: 'linear',\n      times: MAXIMIZE_TIMES,\n    },\n  },\n};\n\nconst occlusionMaskVariants: Variants = {\n  normal: { transform: 'translate(-12px, 12px)' },\n  animate: {\n    transform: [\n      'translate(-12px, 12px)',\n      'translate(-12px, 12px)',\n      'translate(-12px, 12px)',\n      'translate(-12px, 12px)',\n      'translate(-12px, 12px)',\n      'translate(-9px, 9px)',\n      'translate(-4.5px, 4.5px)',\n      'translate(-1px, 1px)',\n      'translate(0px, 0px)',\n      'translate(0px, 0px)',\n      'translate(0px, 0px)',\n      'translate(0px, 0px)',\n      'translate(0px, 0px)',\n    ],\n    transition: {\n      duration: MAXIMIZE_DURATION,\n      ease: 'linear',\n      times: MAXIMIZE_TIMES,\n    },\n  },\n};\n\nconst MaximizeScreenIcon = forwardRef<MaximizeScreenIconHandle, MaximizeScreenIconProps>(\n  ({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {\n    const controls = useAnimation();\n    const occlusionMaskId = 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            <mask\n              id={occlusionMaskId}\n              x=\"0\"\n              y=\"0\"\n              width=\"24\"\n              height=\"24\"\n              maskUnits=\"userSpaceOnUse\"\n            >\n              <rect x=\"0\" y=\"0\" width=\"24\" height=\"24\" fill=\"white\" />\n              <motion.g\n                variants={generatedGeometryVariants}\n                animate={controls}\n                initial=\"normal\"\n              >\n                <motion.path\n                  d={MAXIMIZE_WINDOW}\n                  fill=\"black\"\n                  stroke=\"black\"\n                  strokeLinecap=\"round\"\n                  strokeLinejoin=\"round\"\n                  strokeWidth=\"3\"\n                  variants={occlusionMaskVariants}\n                  animate={controls}\n                  initial=\"normal\"\n                />\n              </motion.g>\n            </mask>\n          </defs>\n          <motion.path\n            d=\"M12.5667 7.93408L15.3088 8.03416C15.7163 8.04903 16.0391 8.38374 16.0391 8.79156V11.4064M10.5391 13.4341L15.5828 8.41565\"\n            stroke=\"currentColor\"\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n            strokeWidth=\"1.5\"\n            variants={arrowVariants}\n            animate={controls}\n            initial=\"normal\"\n            style={{ transformOrigin: '13px 11px' }}\n          />\n          <motion.path\n            d={MAXIMIZE_WINDOW}\n            stroke=\"currentColor\"\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n            strokeWidth=\"1.5\"\n            mask={`url(#${occlusionMaskId})`}\n            variants={sourceWindowVariants}\n            animate={controls}\n            initial=\"normal\"\n          />\n          <motion.path\n            d={MAXIMIZE_OUTER_SOURCE}\n            stroke=\"currentColor\"\n            strokeLinecap=\"round\"\n            strokeWidth=\"1.5\"\n            variants={sourceOuterFrameVariants}\n            animate={controls}\n            initial=\"normal\"\n          />\n          <motion.g\n            variants={generatedGeometryVariants}\n            animate={controls}\n            initial=\"normal\"\n          >\n            <motion.path\n              d={MAXIMIZE_WINDOW}\n              stroke=\"currentColor\"\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n              strokeWidth=\"1.5\"\n              variants={arrivingWindowVariants}\n              animate={controls}\n              initial=\"normal\"\n            />\n          </motion.g>\n        </svg>\n      </div>\n    );\n  }\n);\n\nMaximizeScreenIcon.displayName = 'MaximizeScreenIcon';\n\nexport { MaximizeScreenIcon };\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"
}