| 1 | "use client"; |
| 2 | |
| 3 | import { useCallback, useId, useRef } from "react"; |
| 4 | |
| 5 | interface Props { |
| 6 | size?: number; |
| 7 | } |
| 8 | |
| 9 | export function CanopyLogo({ size = 24 }: Props) { |
| 10 | const svgRef = useRef<SVGSVGElement>(null); |
| 11 | const id = useId(); |
| 12 | const s = id.replace(/:/g, ""); |
| 13 | |
| 14 | const onMouseMove = useCallback((e: React.MouseEvent<SVGSVGElement>) => { |
| 15 | const rect = e.currentTarget.getBoundingClientRect(); |
| 16 | const relX = ((e.clientX - rect.left) / rect.width - 0.5) * 2; |
| 17 | e.currentTarget.style.setProperty("--sway", `${relX * 5}deg`); |
| 18 | }, []); |
| 19 | |
| 20 | const onMouseLeave = useCallback((e: React.MouseEvent<SVGSVGElement>) => { |
| 21 | e.currentTarget.style.setProperty("--sway", "0deg"); |
| 22 | }, []); |
| 23 | |
| 24 | return ( |
| 25 | <svg |
| 26 | ref={svgRef} |
| 27 | className={`canopy-logo-${s}`} |
| 28 | width={size} |
| 29 | height={size} |
| 30 | viewBox="0 0 64 64" |
| 31 | fill="none" |
| 32 | onMouseMove={onMouseMove} |
| 33 | onMouseLeave={onMouseLeave} |
| 34 | > |
| 35 | <style>{` |
| 36 | .canopy-logo-${s} { |
| 37 | cursor: pointer; |
| 38 | transition: transform 0.5s cubic-bezier(0.25, 1, 0.5, 1); |
| 39 | } |
| 40 | .canopy-logo-${s}:hover, |
| 41 | .hover-row:hover .canopy-logo-${s} { |
| 42 | } |
| 43 | .canopy-logo-${s} .cl-bg { |
| 44 | transition: filter 0.6s ease; |
| 45 | } |
| 46 | .canopy-logo-${s}:hover .cl-bg, |
| 47 | .hover-row:hover .canopy-logo-${s} .cl-bg { |
| 48 | filter: brightness(0.9); |
| 49 | } |
| 50 | .canopy-logo-${s} .cl-canopy { |
| 51 | transform-origin: 31px 42px; |
| 52 | transform: rotate(var(--sway, 0deg)); |
| 53 | transition: transform 0.8s cubic-bezier(0.34, 1.56, 0.64, 1); |
| 54 | } |
| 55 | .canopy-logo-${s}:hover .cl-canopy, |
| 56 | .hover-row:hover .canopy-logo-${s} .cl-canopy { |
| 57 | transition: transform 0.15s ease-out; |
| 58 | } |
| 59 | .canopy-logo-${s} .cl-trunk { |
| 60 | transition: stroke-width 0.4s cubic-bezier(0.25, 1, 0.5, 1); |
| 61 | } |
| 62 | .canopy-logo-${s}:hover .cl-trunk, |
| 63 | .hover-row:hover .canopy-logo-${s} .cl-trunk { |
| 64 | stroke-width: 3.2; |
| 65 | } |
| 66 | .canopy-logo-${s} .cl-root { |
| 67 | transition: stroke-width 0.4s cubic-bezier(0.25, 1, 0.5, 1) 0.05s; |
| 68 | } |
| 69 | .canopy-logo-${s}:hover .cl-root, |
| 70 | .hover-row:hover .canopy-logo-${s} .cl-root { |
| 71 | stroke-width: 2.2; |
| 72 | } |
| 73 | `}</style> |
| 74 | |
| 75 | <circle cx="32" cy="32" r="32" fill="var(--accent)" className="cl-bg" /> |
| 76 | <g className="cl-canopy"> |
| 77 | <path className="cl-trunk" d="M31 42 L30 53" stroke="white" strokeWidth="2.5" strokeLinecap="round"/> |
| 78 | <path className="cl-root" d="M30.5 46 L34 49.5" stroke="white" strokeWidth="1.5" strokeLinecap="round"/> |
| 79 | <path d=" |
| 80 | M31 42 |
| 81 | C18 40, 11 32, 14 24 |
| 82 | C16 17, 24 12, 31 14 |
| 83 | C33 10, 40 12, 43 16 |
| 84 | C48 14, 52 22, 49 28 |
| 85 | C52 34, 46 40, 38 40 |
| 86 | C36 42, 33 43, 31 42 |
| 87 | Z |
| 88 | " fill="white"/> |
| 89 | </g> |
| 90 | </svg> |
| 91 | ); |
| 92 | } |
| 93 | |