{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "buttons-flip",
  "type": "registry:ui",
  "title": "Flip Button",
  "description": "A button that flips over on hover to reveal its back face.",
  "dependencies": [
    "@animate-ui/react",
    "cn",
    "motion"
  ],
  "files": [
    {
      "path": "registry/buttons/flip/index.tsx",
      "type": "registry:ui",
      "target": "@components/animate-ui/buttons/flip.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport {\n  motion,\n  type HTMLMotionProps,\n  type Transition,\n  type Variant,\n} from 'motion/react';\n\nimport { Slot, type WithAsChild } from '@animate-ui/react/primitives/slot';\nimport {\n  motionTokens,\n  ReducedMotionConfig,\n  useShouldReduceMotion,\n  useTempoTransition,\n} from '@animate-ui/react/utils/motion';\nimport { getStrictContext } from '@animate-ui/react/utils/strict-context';\nimport { cn } from 'cn';\n\nconst FACE_TRANSITION: Transition = {\n  type: 'spring',\n  stiffness: 280,\n  damping: 20,\n};\n\nconst buildVariant = ({\n  opacity,\n  rotation,\n  offset,\n  isVertical,\n  rotateAxis,\n}: {\n  opacity: number;\n  rotation: number;\n  offset: string | null;\n  isVertical: boolean;\n  rotateAxis: string;\n}): Variant => ({\n  opacity,\n  [rotateAxis]: rotation,\n  ...(isVertical && offset !== null ? { y: offset } : {}),\n  ...(!isVertical && offset !== null ? { x: offset } : {}),\n});\n\ntype FlipDirection = 'top' | 'bottom' | 'left' | 'right';\n\ntype FlipButtonContextType = {\n  from: FlipDirection;\n  isVertical: boolean;\n  rotateAxis: string;\n};\n\nconst [FlipButtonProvider, useFlipButton] =\n  getStrictContext<FlipButtonContextType>('FlipButtonContext');\n\ntype FlipButtonProps = WithAsChild<\n  HTMLMotionProps<'button'> & {\n    from?: FlipDirection;\n    tapScale?: number;\n  }\n>;\n\nfunction FlipButton(props: FlipButtonProps) {\n  return (\n    <ReducedMotionConfig>\n      <FlipButtonContent {...props} />\n    </ReducedMotionConfig>\n  );\n}\n\nfunction FlipButtonContent({\n  from = 'top',\n  tapScale = motionTokens.scale.press,\n  asChild = false,\n  transition,\n  style,\n  className,\n  ...props\n}: FlipButtonProps) {\n  const reduce = useShouldReduceMotion();\n  const defaultTransition = useTempoTransition(motionTokens.spring.press);\n  const isVertical = from === 'top' || from === 'bottom';\n  const rotateAxis = isVertical ? 'rotateX' : 'rotateY';\n\n  const Component = asChild ? Slot : motion.button;\n\n  return (\n    <FlipButtonProvider value={{ from, isVertical, rotateAxis }}>\n      <Component\n        data-slot=\"flip-button\"\n        className={cn(\n          'text-sm font-medium outline-none focus-visible:ring-3 focus-visible:ring-ring/50 rounded-lg',\n          className,\n        )}\n        variants={reduce ? undefined : { initial: { transform: 'scale(1)' } }}\n        initial=\"initial\"\n        whileHover=\"hover\"\n        whileTap={reduce ? undefined : { transform: `scale(${tapScale})` }}\n        transition={transition ?? defaultTransition}\n        style={{\n          display: 'inline-grid',\n          placeItems: 'center',\n          perspective: '1000px',\n          ...style,\n        }}\n        {...props}\n      />\n    </FlipButtonProvider>\n  );\n}\n\ntype FlipButtonFaceProps = WithAsChild<HTMLMotionProps<'span'>>;\n\nfunction FlipButtonFront({\n  transition,\n  asChild = false,\n  style,\n  className,\n  ...props\n}: FlipButtonFaceProps) {\n  const { from, isVertical, rotateAxis } = useFlipButton();\n  const defaultTransition = useTempoTransition(FACE_TRANSITION);\n  const reduce = useShouldReduceMotion();\n\n  const frontOffset = from === 'top' || from === 'left' ? '50%' : '-50%';\n\n  const frontVariants = {\n    initial: buildVariant({\n      opacity: 1,\n      rotation: 0,\n      offset: '0%',\n      isVertical,\n      rotateAxis,\n    }),\n    hover: buildVariant({\n      opacity: 0,\n      rotation: reduce ? 0 : 90,\n      offset: reduce ? '0%' : frontOffset,\n      isVertical,\n      rotateAxis,\n    }),\n  };\n\n  const Component = asChild ? Slot : motion.span;\n\n  return (\n    <Component\n      data-slot=\"flip-button-front\"\n      className={cn(\n        'h-10 rounded-lg bg-primary px-4 text-primary-foreground',\n        className,\n      )}\n      variants={frontVariants}\n      transition={transition ?? defaultTransition}\n      style={{\n        gridArea: '1 / 1',\n        display: 'inline-flex',\n        alignItems: 'center',\n        justifyContent: 'center',\n        ...style,\n      }}\n      {...props}\n    />\n  );\n}\n\nfunction FlipButtonBack({\n  transition,\n  asChild = false,\n  style,\n  className,\n  ...props\n}: FlipButtonFaceProps) {\n  const { from, isVertical, rotateAxis } = useFlipButton();\n  const defaultTransition = useTempoTransition(FACE_TRANSITION);\n  const reduce = useShouldReduceMotion();\n\n  const backOffset = from === 'top' || from === 'left' ? '-50%' : '50%';\n\n  const backVariants = {\n    initial: buildVariant({\n      opacity: 0,\n      rotation: reduce ? 0 : 90,\n      offset: reduce ? '0%' : backOffset,\n      isVertical,\n      rotateAxis,\n    }),\n    hover: buildVariant({\n      opacity: 1,\n      rotation: 0,\n      offset: '0%',\n      isVertical,\n      rotateAxis,\n    }),\n  };\n\n  const Component = asChild ? Slot : motion.span;\n\n  return (\n    <Component\n      data-slot=\"flip-button-back\"\n      className={cn('h-10 rounded-lg bg-muted px-4 text-foreground', className)}\n      aria-hidden\n      variants={backVariants}\n      transition={transition ?? defaultTransition}\n      style={{\n        gridArea: '1 / 1',\n        display: 'inline-flex',\n        alignItems: 'center',\n        justifyContent: 'center',\n        ...style,\n      }}\n      {...props}\n    />\n  );\n}\n\nexport {\n  FlipButton,\n  FlipButtonFront,\n  FlipButtonBack,\n  useFlipButton,\n  type FlipButtonProps,\n  type FlipButtonFaceProps as FlipButtonFrontProps,\n  type FlipButtonFaceProps as FlipButtonBackProps,\n  type FlipDirection,\n  type FlipButtonContextType,\n};\n"
    }
  ]
}