chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
|
||||
import CurvyRect from "@/components/shared/layout/curvy-rect";
|
||||
import { cn } from "@/utils/cn";
|
||||
|
||||
interface FlameButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
variant?: "primary" | "secondary";
|
||||
}
|
||||
|
||||
// Flame patterns for hover effect - designed to tile and fill the button
|
||||
const flamePatterns = [
|
||||
`▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
|
||||
▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
|
||||
▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
|
||||
▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
|
||||
▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲ `,
|
||||
` ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
|
||||
▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
|
||||
▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
|
||||
▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
|
||||
▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲ `,
|
||||
`▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
|
||||
▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
|
||||
▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
|
||||
▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
|
||||
▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲`,
|
||||
];
|
||||
|
||||
export function FlameButton({
|
||||
children,
|
||||
className,
|
||||
variant = "primary",
|
||||
onMouseDown,
|
||||
onMouseUp,
|
||||
onMouseLeave,
|
||||
...props
|
||||
}: FlameButtonProps) {
|
||||
const [flameIndex, setFlameIndex] = useState(0);
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const [isPressed, setIsPressed] = useState(false);
|
||||
const intervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (isHovered || isPressed) {
|
||||
const speed = isPressed ? 80 : 150; // Faster when pressed
|
||||
intervalRef.current = setInterval(() => {
|
||||
setFlameIndex((prev) => (prev + 1) % flamePatterns.length);
|
||||
}, speed);
|
||||
} else {
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
}
|
||||
};
|
||||
}, [isHovered, isPressed]);
|
||||
|
||||
const handleMouseDown = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setIsPressed(true);
|
||||
onMouseDown?.(e);
|
||||
};
|
||||
|
||||
const handleMouseUp = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setIsPressed(false);
|
||||
onMouseUp?.(e);
|
||||
};
|
||||
|
||||
const handleMouseLeave = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setIsPressed(false);
|
||||
setIsHovered(false);
|
||||
onMouseLeave?.(e);
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
className={cn(
|
||||
"relative overflow-hidden rounded-8 transition-all text-body-medium",
|
||||
"bg-white border border-heat-100 text-heat-100",
|
||||
"hover:border-heat-200 hover:shadow-[0_0_20px_rgba(250,93,25,0.2)]",
|
||||
"active:shadow-[0_0_30px_rgba(250,93,25,0.4)]",
|
||||
className,
|
||||
)}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
onMouseDown={handleMouseDown}
|
||||
onMouseUp={handleMouseUp}
|
||||
{...props}
|
||||
>
|
||||
<CurvyRect className="absolute inset-0 pointer-events-none" allSides />
|
||||
|
||||
{/* Animated flame background on hover/click */}
|
||||
{(isHovered || isPressed) && (
|
||||
<div className="absolute inset-0 pointer-events-none select-none overflow-hidden">
|
||||
<div
|
||||
className={cn(
|
||||
"absolute inset-0 flex items-center justify-center transition-opacity duration-200",
|
||||
isPressed ? "opacity-25" : "opacity-10",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className="text-heat-100 font-mono leading-none w-full h-full"
|
||||
style={{
|
||||
fontSize: "3px",
|
||||
lineHeight: "3px",
|
||||
whiteSpace: "pre",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
transform: isPressed ? "scale(1.1)" : "scale(1)",
|
||||
transition: "transform 0.1s ease",
|
||||
textShadow: isPressed
|
||||
? "0 0 3px rgba(250,93,25,0.8)"
|
||||
: "0 0 1px rgba(250,93,25,0.5)",
|
||||
}}
|
||||
>
|
||||
{flamePatterns[flameIndex]}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<span className="relative z-10 flex items-center justify-center">
|
||||
{children}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user