Merge version_2 into main #4

Merged
bender merged 12 commits from version_2 into main 2026-02-27 12:35:30 +00:00
Showing only changes of commit e4323271f3 - Show all commits

View File

@@ -1,186 +1,151 @@
"use client";
import { memo } from "react";
import CardStack from "@/components/cardStack/CardStack";
import { cls, shouldUseInvertedText } from "@/lib/utils";
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
import type { LucideIcon } from "lucide-react";
import type { ButtonConfig, CardAnimationTypeWith3D, TitleSegment, ButtonAnimationType } from "@/components/cardStack/types";
import type { TextboxLayout, InvertedBackground } from "@/providers/themeProvider/config/constants";
import React, { useState } from "react";
import { LucideIcon } from "lucide-react";
type Metric = {
id: string;
icon: LucideIcon;
title: string;
value: string;
};
interface MetricCardThreeProps {
metrics: Metric[];
carouselMode?: "auto" | "buttons";
uniformGridCustomHeightClasses?: string;
animationType: CardAnimationTypeWith3D;
title: string;
titleSegments?: TitleSegment[];
description: string;
tag?: string;
tagIcon?: LucideIcon;
tagAnimation?: ButtonAnimationType;
buttons?: ButtonConfig[];
buttonAnimation?: ButtonAnimationType;
textboxLayout: TextboxLayout;
useInvertedBackground: InvertedBackground;
ariaLabel?: string;
className?: string;
containerClassName?: string;
cardClassName?: string;
textBoxTitleClassName?: string;
textBoxTitleImageWrapperClassName?: string;
textBoxTitleImageClassName?: string;
textBoxDescriptionClassName?: string;
iconContainerClassName?: string;
iconClassName?: string;
metricTitleClassName?: string;
valueClassName?: string;
gridClassName?: string;
carouselClassName?: string;
controlsClassName?: string;
textBoxClassName?: string;
textBoxTagClassName?: string;
textBoxButtonContainerClassName?: string;
textBoxButtonClassName?: string;
textBoxButtonTextClassName?: string;
interface Metric {
id: string;
icon: LucideIcon;
title: string;
value: string;
}
interface MetricCardItemProps {
metric: Metric;
shouldUseLightText: boolean;
cardClassName?: string;
iconContainerClassName?: string;
iconClassName?: string;
metricTitleClassName?: string;
valueClassName?: string;
interface Button {
text: string;
href: string;
}
const MetricCardItem = memo(({
metric,
shouldUseLightText,
cardClassName = "",
iconContainerClassName = "",
iconClassName = "",
metricTitleClassName = "",
valueClassName = "",
}: MetricCardItemProps) => {
return (
<div className={cls("relative h-full card text-foreground rounded-theme-capped p-6 flex flex-col items-center justify-center gap-3", cardClassName)}>
<div className="relative z-1 w-full flex items-center justify-center gap-2">
<div className={cls("h-8 primary-button aspect-square rounded-theme flex items-center justify-center", iconContainerClassName)}>
<metric.icon className={cls("h-4/10 text-primary-cta-text", iconClassName)} strokeWidth={1.5} />
</div>
<h3 className={cls("text-xl truncate", shouldUseLightText ? "text-background" : "text-foreground", metricTitleClassName)}>
{metric.title}
</h3>
</div>
<div className="relative z-1 w-full flex items-center justify-center">
<h4 className={cls("text-7xl font-medium truncate", shouldUseLightText ? "text-background" : "text-foreground", valueClassName)}>
{metric.value}
</h4>
</div>
interface Props {
title: string;
description: string;
tag: string;
tagIcon: LucideIcon;
tagAnimation?: string;
metrics: Metric[];
textboxLayout?: string;
animationType?: string;
useInvertedBackground?: boolean;
buttons?: Button[];
buttonAnimation?: string;
}
export default function MetricCardThree({
title,
description,
tag,
tagIcon: TagIcon,
metrics,
useInvertedBackground = false,
buttons = [],
}: Props) {
const [flipped, setFlipped] = useState<{ [key: string]: boolean }>({});
const toggleFlip = (id: string) => {
setFlipped((prev) => ({
...prev,
[id]: !prev[id],
}));
};
const bgClass = useInvertedBackground
? "bg-slate-900 text-white"
: "bg-white text-slate-900";
return (
<section className={`py-16 px-4 sm:px-6 lg:px-8 ${bgClass}`}>
<div className="max-w-6xl mx-auto">
{/* Header Section */}
<div className="mb-12 text-center">
<div className="inline-flex items-center gap-2 mb-4 px-4 py-2 rounded-full bg-blue-100 text-blue-700">
<TagIcon className="w-4 h-4" />
<span className="text-sm font-semibold">{tag}</span>
</div>
<h2 className="text-4xl sm:text-5xl font-bold mb-4">{title}</h2>
<p className="text-lg text-slate-600 max-w-2xl mx-auto">
{description}
</p>
</div>
);
});
MetricCardItem.displayName = "MetricCardItem";
{/* Metrics Grid with Flip Cards */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
{metrics.map((metric) => {
const MetricIcon = metric.icon;
const isFlipped = flipped[metric.id] || false;
const MetricCardThree = ({
metrics,
carouselMode = "buttons",
uniformGridCustomHeightClasses = "min-h-70 2xl:min-h-80",
animationType,
title,
titleSegments,
description,
tag,
tagIcon,
tagAnimation,
buttons,
buttonAnimation,
textboxLayout,
useInvertedBackground,
ariaLabel = "Metrics section",
className = "",
containerClassName = "",
cardClassName = "",
textBoxTitleClassName = "",
textBoxTitleImageWrapperClassName = "",
textBoxTitleImageClassName = "",
textBoxDescriptionClassName = "",
iconContainerClassName = "",
iconClassName = "",
metricTitleClassName = "",
valueClassName = "",
gridClassName = "",
carouselClassName = "",
controlsClassName = "",
textBoxClassName = "",
textBoxTagClassName = "",
textBoxButtonContainerClassName = "",
textBoxButtonClassName = "",
textBoxButtonTextClassName = "",
}: MetricCardThreeProps) => {
const theme = useTheme();
const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle);
return (
<div
key={metric.id}
className="h-64 cursor-pointer perspective"
onClick={() => toggleFlip(metric.id)}
>
<div
className={`relative w-full h-full transition-transform duration-500 transform-gpu ${
isFlipped ? "rotate-y-180" : ""
}`}
style={{
transformStyle: "preserve-3d",
transform: isFlipped
? "rotateY(180deg)"
: "rotateY(0deg)",
}}
>
{/* Front of Card */}
<div
className="absolute w-full h-full bg-gradient-to-br from-blue-50 to-blue-100 rounded-lg p-6 flex flex-col items-center justify-center text-center border border-blue-200 shadow-lg"
style={{
backfaceVisibility: "hidden",
}}
>
<MetricIcon className="w-12 h-12 text-blue-600 mb-4" />
<h3 className="text-sm font-semibold text-slate-700 mb-3">
{metric.title}
</h3>
<p className="text-4xl font-bold text-blue-600">
{metric.value}
</p>
<p className="text-xs text-slate-500 mt-4">
Click to see more
</p>
</div>
return (
<CardStack
useInvertedBackground={useInvertedBackground}
mode={carouselMode}
gridVariant="uniform-all-items-equal"
uniformGridCustomHeightClasses={uniformGridCustomHeightClasses}
animationType={animationType}
supports3DAnimation={true}
{/* Back of Card */}
<div
className="absolute w-full h-full bg-gradient-to-br from-slate-800 to-slate-900 rounded-lg p-6 flex flex-col items-center justify-center text-center border border-slate-700 shadow-lg text-white"
style={{
backfaceVisibility: "hidden",
transform: "rotateY(180deg)",
}}
>
<p className="text-sm leading-relaxed mb-4">
{metric.title} represents our commitment to excellence and
continuous growth in the computer club community.
</p>
<p className="text-xs text-slate-400">
Click to flip back
</p>
</div>
</div>
</div>
);
})}
</div>
title={title}
titleSegments={titleSegments}
description={description}
tag={tag}
tagIcon={tagIcon}
tagAnimation={tagAnimation}
buttons={buttons}
buttonAnimation={buttonAnimation}
textboxLayout={textboxLayout}
className={className}
containerClassName={containerClassName}
gridClassName={gridClassName}
carouselClassName={carouselClassName}
controlsClassName={controlsClassName}
textBoxClassName={textBoxClassName}
titleClassName={textBoxTitleClassName}
titleImageWrapperClassName={textBoxTitleImageWrapperClassName}
titleImageClassName={textBoxTitleImageClassName}
descriptionClassName={textBoxDescriptionClassName}
tagClassName={textBoxTagClassName}
buttonContainerClassName={textBoxButtonContainerClassName}
buttonClassName={textBoxButtonClassName}
buttonTextClassName={textBoxButtonTextClassName}
ariaLabel={ariaLabel}
>
{metrics.map((metric, index) => (
<MetricCardItem
key={`${metric.id}-${index}`}
metric={metric}
shouldUseLightText={shouldUseLightText}
cardClassName={cardClassName}
iconContainerClassName={iconContainerClassName}
iconClassName={iconClassName}
metricTitleClassName={metricTitleClassName}
valueClassName={valueClassName}
/>
{/* Buttons Section */}
{buttons.length > 0 && (
<div className="flex flex-wrap gap-4 justify-center">
{buttons.map((button, index) => (
<a
key={index}
href={button.href}
className="px-8 py-3 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 transition-colors duration-300 shadow-md hover:shadow-lg"
>
{button.text}
</a>
))}
</CardStack>
);
};
MetricCardThree.displayName = "MetricCardThree";
export default MetricCardThree;
</div>
)}
</div>
</section>
);
}