Spaces:
Sleeping
Sleeping
File size: 1,863 Bytes
b64de39 4995d62 b64de39 4995d62 b64de39 4995d62 b64de39 4995d62 b64de39 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | /**
* Card - Container card with variant support.
*
* Variants: default | bordered | elevated | flat
* Optional header, footer, and hover-lift effect with smooth
* transform animation on hover.
*/
import PropTypes from "prop-types";
const VARIANT_CLASSES = {
default: "bg-white shadow-card border border-neutral-200",
bordered: "bg-white border-2 border-neutral-300",
elevated: "bg-white shadow-card-hover",
flat: "bg-neutral-100",
};
function Card({
children,
variant = "default",
header,
footer,
hoverable = false,
padding = true,
className = "",
onClick,
...rest
}) {
const isClickable = Boolean(onClick);
const shouldLift = hoverable || isClickable;
const classes = [
"rounded-xl overflow-hidden",
"transition-all duration-200 ease-out",
VARIANT_CLASSES[variant] || VARIANT_CLASSES.default,
shouldLift ? "hover-lift" : "",
isClickable ? "cursor-pointer" : "",
className,
]
.filter(Boolean)
.join(" ");
const Tag = isClickable ? "button" : "div";
return (
<Tag
className={classes}
onClick={onClick}
role={isClickable ? "button" : undefined}
tabIndex={isClickable ? 0 : undefined}
{...rest}
>
{header && (
<div className="border-b border-neutral-200 px-5 py-4">{header}</div>
)}
<div className={padding ? "p-5" : ""}>{children}</div>
{footer && (
<div className="border-t border-neutral-200 px-5 py-3 bg-neutral-50">
{footer}
</div>
)}
</Tag>
);
}
Card.propTypes = {
children: PropTypes.node.isRequired,
variant: PropTypes.oneOf(["default", "bordered", "elevated", "flat"]),
header: PropTypes.node,
footer: PropTypes.node,
hoverable: PropTypes.bool,
padding: PropTypes.bool,
className: PropTypes.string,
onClick: PropTypes.func,
};
export default Card;
|