import React, { useState, useCallback } from 'react'; export interface CardOffgridProps { /** Color variant of the card */ variant?: 'neutral' | 'green'; /** Icon element or image source */ icon: React.ReactNode | string; /** Card title (supports multi-line via \n) */ title: string; /** Card description text */ description: string; /** Click handler */ onClick?: () => void; /** Link destination (renders as anchor if provided) */ href?: string; /** Disabled state */ disabled?: boolean; /** Optional className for custom styling */ className?: string; } /** * BDS CardOffgrid Component * * A versatile card component for displaying feature highlights with an icon, * title, and description. Supports neutral and green color variants with * interactive states (hover, focus, pressed, disabled). * * Features a "window shade" color wipe animation: * - Hover in: shade rises from bottom to top (reveals hover color) * - Hover out: shade falls from top to bottom (hides hover color) * * @example * // Basic neutral card * } * title="Onchain Metadata" * description="Easily store key asset information." * onClick={() => console.log('clicked')} * /> * * @example * // Green card with link * */ export const CardOffgrid: React.FC = ({ variant = 'neutral', icon, title, description, onClick, href, disabled = false, className = '', }) => { // Track hover state for animation const [isHovered, setIsHovered] = useState(false); const handleMouseEnter = useCallback(() => { if (!disabled) { setIsHovered(true); } }, [disabled]); const handleMouseLeave = useCallback(() => { if (!disabled) { setIsHovered(false); } }, [disabled]); // Build class names using BEM with bds namespace const classNames = [ 'bds-card-offgrid', `bds-card-offgrid--${variant}`, disabled && 'bds-card-offgrid--disabled', isHovered && 'bds-card-offgrid--hovered', className, ] .filter(Boolean) .join(' '); // Render icon - supports both React nodes and image URLs const renderIcon = () => { if (typeof icon === 'string') { return ( ); } return icon; }; // Split title by newline for multi-line support const renderTitle = () => { const lines = title.split('\n'); return lines.map((line, index) => ( {line} {index < lines.length - 1 &&
}
)); }; // Common content for both button and anchor const content = ( <> {/* Hover color wipe overlay */}