163 lines
4.2 KiB
TypeScript
163 lines
4.2 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
|
|
interface TitleSegment {
|
|
text: string;
|
|
highlight?: boolean;
|
|
}
|
|
|
|
interface Button {
|
|
label: string;
|
|
href?: string;
|
|
onClick?: () => void;
|
|
variant?: "primary" | "secondary";
|
|
}
|
|
|
|
interface CardStackTextBoxProps {
|
|
title: string;
|
|
titleSegments?: TitleSegment[];
|
|
description: string;
|
|
tag?: string;
|
|
tagIcon?: React.ReactNode;
|
|
tagAnimation?: string;
|
|
buttons?: Button[];
|
|
buttonAnimation?: string;
|
|
textboxLayout?: "default" | "centered" | "compact";
|
|
useInvertedBackground?: boolean;
|
|
className?: string;
|
|
titleClassName?: string;
|
|
titleImageWrapperClassName?: string;
|
|
titleImageClassName?: string;
|
|
descriptionClassName?: string;
|
|
tagClassName?: string;
|
|
buttonContainerClassName?: string;
|
|
buttonClassName?: string;
|
|
buttonTextClassName?: string;
|
|
}
|
|
|
|
export function CardStackTextBox({
|
|
title,
|
|
titleSegments,
|
|
description,
|
|
tag,
|
|
tagIcon,
|
|
tagAnimation,
|
|
buttons,
|
|
buttonAnimation,
|
|
textboxLayout = "default",
|
|
useInvertedBackground = false,
|
|
className = "",
|
|
titleClassName = "",
|
|
titleImageWrapperClassName = "",
|
|
titleImageClassName = "",
|
|
descriptionClassName = "",
|
|
tagClassName = "",
|
|
buttonContainerClassName = "",
|
|
buttonClassName = "",
|
|
buttonTextClassName = "",
|
|
}: CardStackTextBoxProps) {
|
|
const baseClasses = `
|
|
w-full h-full p-6 flex flex-col justify-between
|
|
${useInvertedBackground ? "bg-slate-900 text-white" : "bg-white text-slate-900"}
|
|
rounded-lg transition-all duration-300
|
|
`;
|
|
|
|
const layoutClasses = {
|
|
default: "items-start",
|
|
centered: "items-center text-center",
|
|
compact: "items-start gap-2",
|
|
};
|
|
|
|
const titleDefaultClasses = `
|
|
text-2xl font-bold mb-4 leading-tight
|
|
${titleClassName}
|
|
`;
|
|
|
|
const descriptionDefaultClasses = `
|
|
text-base leading-relaxed mb-4 flex-grow
|
|
${useInvertedBackground ? "text-slate-300" : "text-slate-600"}
|
|
${descriptionClassName}
|
|
`;
|
|
|
|
const tagDefaultClasses = `
|
|
inline-flex items-center gap-2 px-3 py-1 rounded-full text-sm font-medium mb-4
|
|
${useInvertedBackground ? "bg-slate-800 text-slate-200" : "bg-slate-100 text-slate-700"}
|
|
${tagClassName}
|
|
`;
|
|
|
|
const buttonContainerDefaultClasses = `
|
|
flex gap-3 w-full
|
|
${textboxLayout === "centered" ? "justify-center" : "justify-start"}
|
|
${buttonContainerClassName}
|
|
`;
|
|
|
|
const buttonDefaultClasses = `
|
|
px-4 py-2 rounded-lg font-medium transition-all duration-200
|
|
${buttonClassName}
|
|
`;
|
|
|
|
const buttonTextDefaultClasses = `
|
|
${buttonTextClassName}
|
|
`;
|
|
|
|
const renderTitle = () => {
|
|
if (titleSegments && titleSegments.length > 0) {
|
|
return (
|
|
<h3 className={titleDefaultClasses}>
|
|
{titleSegments.map((segment, index) => (
|
|
<span
|
|
key={index}
|
|
className={segment.highlight ? "text-blue-500" : ""}
|
|
>
|
|
{segment.text}
|
|
</span>
|
|
))}
|
|
</h3>
|
|
);
|
|
}
|
|
return <h3 className={titleDefaultClasses}>{title}</h3>;
|
|
};
|
|
|
|
return (
|
|
<div className={`${baseClasses} ${layoutClasses[textboxLayout]} ${className}`}>
|
|
<div className="w-full">
|
|
{tag && (
|
|
<div className={tagDefaultClasses}>
|
|
{tagIcon && <span>{tagIcon}</span>}
|
|
<span>{tag}</span>
|
|
</div>
|
|
)}
|
|
|
|
{renderTitle()}
|
|
|
|
<p className={descriptionDefaultClasses}>{description}</p>
|
|
</div>
|
|
|
|
{buttons && buttons.length > 0 && (
|
|
<div className={buttonContainerDefaultClasses}>
|
|
{buttons.map((button, index) => (
|
|
<button
|
|
key={index}
|
|
onClick={button.onClick}
|
|
className={`
|
|
${buttonDefaultClasses}
|
|
${
|
|
button.variant === "secondary"
|
|
? useInvertedBackground
|
|
? "bg-slate-700 hover:bg-slate-600 text-white"
|
|
: "bg-slate-200 hover:bg-slate-300 text-slate-900"
|
|
: useInvertedBackground
|
|
? "bg-blue-600 hover:bg-blue-700 text-white"
|
|
: "bg-blue-500 hover:bg-blue-600 text-white"
|
|
}
|
|
`}
|
|
>
|
|
<span className={buttonTextDefaultClasses}>{button.label}</span>
|
|
</button>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
} |