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 */}
{renderIcon()}
{renderTitle()}
{description}
>
);
// Render as anchor if href is provided
if (href && !disabled) {
return (
{content}
);
}
// Render as button for onClick or disabled state
return (
);
};
export default CardOffgrid;