Files
rheinsw-mono-repo/frontend/components/PulsatingButton.tsx

137 lines
3.6 KiB
TypeScript

'use client';
import React from 'react';
import Link from 'next/link';
import styled, {keyframes} from 'styled-components';
type PulsatingButtonProps = {
label: string;
href: string;
color?: string;
textColor?: string;
width?: number;
height?: number;
pulse?: boolean;
};
const MAX_LAYERS = 2;
const pulse = keyframes`
0%, 10% {
opacity: 0;
transform: scale(1.1);
}
80% {
opacity: 0.7;
transform: scale(1.15, 1.4);
}
81%, 100% {
opacity: 0;
transform: scale(1);
}
`;
const scale = keyframes`
0% {
transform: scale(1);
}
35%, 80% {
transform: scale(1.1, 1.35);
}
100% {
transform: scale(1);
}
`;
const Wrapper = styled.div`
position: relative;
display: inline-flex;
justify-content: center;
align-items: center;
`;
const Layer = styled.div<{
color: string;
$width: number;
$height: number;
$layer: number;
}>`
position: absolute;
background: transparent;
border: 1px solid ${({color, $layer}) =>
$layer === 0 ? `${color}AA` : `${color}66`}; // L0: ~67%, L1: ~40%
border-radius: 9999px;
animation: ${({$layer}) => ($layer ? pulse : scale)} 1.5s infinite;
width: ${({$width, $layer}) => $width + $layer * 8}px;
height: ${({$height, $layer}) => $height + $layer * 8}px;
z-index: ${({$layer}) => MAX_LAYERS - $layer};
`;
// Use $-prefix to avoid prop leaking
const StyledButton = styled.a.withConfig({
shouldForwardProp: (prop) =>
!['$bgColor', '$textColor', '$width', '$height'].includes(prop),
})<{
$bgColor: string;
$textColor: string;
$width: number;
$height: number;
}>`
position: relative;
z-index: ${MAX_LAYERS + 1};
display: inline-flex;
justify-content: center;
align-items: center;
background-color: ${({$bgColor}) => $bgColor};
color: ${({$textColor}) => $textColor};
border-radius: 9999px;
padding: 0.75rem 1.5rem;
font-weight: 600;
text-decoration: none;
transition: background-color 0.3s ease;
width: ${({$width}) => $width}px;
height: ${({$height}) => $height}px;
&:hover {
opacity: 0.9;
}
`;
const PulsatingButton: React.FC<PulsatingButtonProps> = ({
label,
href,
color = '#3B82F6', // Tailwind blue-500
textColor = '#ffffff',
width = 160,
height = 48,
pulse = true,
}) => {
return (
<Wrapper>
{pulse &&
Array.from({length: MAX_LAYERS}).map((_, index) => (
<Layer
key={index}
color={color}
$width={width}
$height={height}
$layer={index}
/>
))}
<Link href={href} passHref legacyBehavior>
<StyledButton
$bgColor={color}
$textColor={textColor}
$width={width}
$height={height}
>
{label}
</StyledButton>
</Link>
</Wrapper>
);
};
export default PulsatingButton;