74 lines
1.9 KiB
TypeScript
74 lines
1.9 KiB
TypeScript
import type { ReactNode } from 'react'
|
|
|
|
export type CardVariant = 'default' | 'interactive' | 'selected' | 'compact'
|
|
|
|
interface CardProps {
|
|
children: ReactNode
|
|
variant?: CardVariant
|
|
className?: string
|
|
onClick?: () => void
|
|
'aria-label'?: string
|
|
}
|
|
|
|
function getVariantClasses(variant: CardVariant, hasOnClick: boolean): string {
|
|
const baseClasses = 'border rounded-lg bg-cyber-dark'
|
|
|
|
switch (variant) {
|
|
case 'default':
|
|
return `${baseClasses} border-neon-cyan/30`
|
|
case 'interactive':
|
|
return `${baseClasses} border-neon-cyan/30 hover:border-neon-cyan/50 hover:shadow-glow-cyan transition-all ${hasOnClick ? 'cursor-pointer' : ''}`
|
|
case 'selected':
|
|
return `${baseClasses} border-neon-cyan ring-1 ring-neon-cyan/50 shadow-glow-cyan`
|
|
case 'compact':
|
|
return `${baseClasses} border-neon-cyan/30 p-4`
|
|
default:
|
|
return `${baseClasses} border-neon-cyan/30`
|
|
}
|
|
}
|
|
|
|
function getPaddingClasses(variant: CardVariant): string {
|
|
if (variant === 'compact') {
|
|
return 'p-4'
|
|
}
|
|
return 'p-6'
|
|
}
|
|
|
|
export function Card({
|
|
children,
|
|
variant = 'default',
|
|
className = '',
|
|
onClick,
|
|
'aria-label': ariaLabel,
|
|
}: CardProps): React.ReactElement {
|
|
const variantClasses = getVariantClasses(variant, onClick !== undefined)
|
|
const paddingClasses = getPaddingClasses(variant)
|
|
const combinedClasses = `${variantClasses} ${paddingClasses} ${className}`.trim()
|
|
|
|
if (onClick) {
|
|
return (
|
|
<div
|
|
onClick={onClick}
|
|
className={combinedClasses}
|
|
role="button"
|
|
tabIndex={0}
|
|
onKeyDown={(e) => {
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
e.preventDefault()
|
|
onClick()
|
|
}
|
|
}}
|
|
aria-label={ariaLabel}
|
|
>
|
|
{children}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className={combinedClasses} aria-label={ariaLabel}>
|
|
{children}
|
|
</div>
|
|
)
|
|
}
|