Files
netrunner-landing/components/ui/cyber-button.tsx
T
2026-07-02 23:13:24 +07:00

117 lines
4.0 KiB
TypeScript

"use client";
import Link from "next/link";
import { motion } from "framer-motion";
import { cn } from "@/lib/utils";
interface CyberButtonProps {
href?: string;
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
children: React.ReactNode;
className?: string;
variant?: "primary" | "secondary";
disabled?: boolean;
type?: "button" | "submit" | "reset";
}
/**
* Кастомная акцентная кнопка темы проекта (со скобками-маркерами `</` и `/>` вокруг
* текста и бегущим бликом при ховере). Два варианта оформления:
* - "primary" — заливка фиолетовым (#8B3DFF), основной CTA (скачать, отправить и т.д.);
* - "secondary" — прозрачная с цановой (#00E5F2) обводкой, второстепенные действия.
* Может рендериться и как ссылка (если передан href — оборачивается в next/link),
* и как обычная <button> (для форм, например submit в гостевой книге).
*/
export function CyberButton({
href,
onClick,
children,
className,
variant = "primary",
disabled = false,
type = "button",
}: CyberButtonProps) {
const isPrimary = variant === "primary";
const content = (
<motion.div
whileHover={!disabled ? { scale: 1.02 } : {}}
whileTap={!disabled ? { scale: 0.98 } : {}}
className={cn(
"relative z-10 flex items-center justify-center px-6 py-3 overflow-hidden transition-all duration-300 border rounded-xl font-mono", // Уменьшил padding
!disabled && [
isPrimary
? "bg-[#8B3DFF] border-[#8B3DFF] text-white shadow-lg shadow-purple-500/20 dark:bg-[radial-gradient(circle_at_center,_rgba(139,61,255,0.4)_0%,_rgba(139,61,255,0.1)_100%)] dark:border-[#8B3DFF]/50 dark:shadow-[0_0_25px_rgba(139,61,255,0.3)]"
: "text-[#0284c7] dark:text-[#00E5F2] border-[#0284c7]/40 dark:border-[#00E5F2]/40 backdrop-blur-md bg-slate-900/5 dark:bg-[#00E5F2]/5",
],
disabled && "cursor-not-allowed opacity-50 border-dashed",
className,
)}
>
{/* Энергетический блик */}
{!disabled && (
<div className="absolute inset-0 w-full h-full -translate-x-full group-hover:animate-scan pointer-events-none bg-gradient-to-r from-transparent via-white/10 to-transparent" />
)}
{/* Улучшенные скобки-маркеры */}
<div className="flex items-center gap-3">
<span
className={cn(
"text-[10px] font-bold transition-transform group-hover:-translate-x-0.5",
isPrimary ? "text-white/50" : "text-secondary/50",
)}
>
&lt;/
</span>
<span className="relative z-20 font-black uppercase tracking-wider text-xs flex items-center gap-2">
{children}
</span>
<span
className={cn(
"text-[10px] font-bold transition-transform group-hover:translate-x-0.5",
isPrimary ? "text-white/50" : "text-secondary/50",
)}
>
/&gt;
</span>
</div>
</motion.div>
);
const glowEffect = !disabled && (
<div
className={cn(
"absolute inset-0 blur-xl opacity-0 group-hover:opacity-30 transition-opacity duration-500 -z-10",
isPrimary ? "bg-[#8B3DFF]" : "bg-[#00E5F2]",
)}
/>
);
if (href && !disabled) {
return (
<Link
href={href}
onClick={(e) => onClick?.(e as any)}
className="group relative inline-block"
>
{content}
{glowEffect}
</Link>
);
}
return (
<button
type={type}
disabled={disabled}
onClick={onClick}
className="group relative inline-block bg-transparent border-none p-0 outline-none cursor-pointer"
>
{content}
{glowEffect}
</button>
);
}