import type { Ref } from 'react' import { motion, useReducedMotion } from 'motion/react' import type { IconProps } from '@tabler/icons-react' import { cn } from '@/client/lib/cn' type IconGhostProps = IconProps & { animated?: boolean ref?: Ref } type Point = { x: number y: number } const bodyPath = 'M5 11a7 7 0 0 1 14 0v7a1.78 1.78 0 0 1 -3.1 1.4a1.65 1.65 0 0 0 -2.6 0a1.65 1.65 0 0 1 -2.6 0a1.65 1.65 0 0 0 -2.6 0a1.78 1.78 0 0 1 -3.1 -1.4v-7' const animatedEdgeY = 18.85 function formatPoint(value: number): string { return value.toFixed(3) } function smoothPath(points: Point[]): string { let path = '' for (let index = 0; index < points.length - 1; index += 1) { const previous = points[index - 1] ?? points[index] const current = points[index] const next = points[index + 1] const afterNext = points[index + 2] ?? next const firstControl = index === 0 ? { x: current.x, y: current.y + 0.4 } : { x: current.x + (next.x - previous.x) / 6, y: current.y + (next.y - previous.y) / 6 } const secondControl = index === points.length - 2 ? { x: next.x, y: next.y + 0.4 } : { x: next.x - (afterNext.x - current.x) / 6, y: next.y - (afterNext.y - current.y) / 6 } path += `C${formatPoint(firstControl.x)} ${formatPoint(firstControl.y)} ${formatPoint(secondControl.x)} ${formatPoint(secondControl.y)} ${formatPoint(next.x)} ${formatPoint(next.y)}` } return path } function bodyPathAt(progress: number): string { const phase = progress * Math.PI * 2 const verticalProgress = 1 - Math.abs(progress * 2 - 1) const verticalOffset = verticalProgress * 1.55 const edgeY = animatedEdgeY + verticalOffset const points = Array.from({ length: 13 }, (_, index): Point => { const horizontalProgress = index / 12 const envelope = Math.sin(Math.PI * horizontalProgress) ** 0.35 const baseline = edgeY + 0.3 * envelope const wave = 0.55 * envelope * Math.sin(5 * Math.PI * horizontalProgress - phase) return { x: 5 + 14 * horizontalProgress, y: baseline + wave } }) const sideLength = edgeY - 11 return `M5 11a7 7 0 0 1 14 0v${formatPoint(sideLength)}${smoothPath(points.reverse())}v-${formatPoint(sideLength)}` } const animatedBodyPaths = Array.from({ length: 17 }, (_, index) => bodyPathAt(index / 16)) function IconGhost({ animated = false, color = 'currentColor', size = 24, stroke = 2, title, className, children, ref, ...props }: IconGhostProps) { const prefersReducedMotion = useReducedMotion() const shouldAnimate = animated && !prefersReducedMotion return ( {title && {title}} {shouldAnimate ? ( ) : ( )} {children} ) } export { IconGhost }