Update src/components/sections/about/AboutMetric.tsx
This commit is contained in:
@@ -1,106 +1,116 @@
|
|||||||
"use client";
|
'use client';
|
||||||
|
|
||||||
import TextAnimation from "@/components/text/TextAnimation";
|
import React, { useState } from 'react';
|
||||||
import { cls, shouldUseInvertedText } from "@/lib/utils";
|
import { LucideIcon } from 'lucide-react';
|
||||||
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
|
|
||||||
import { useButtonAnimation } from "@/components/hooks/useButtonAnimation";
|
|
||||||
import type { LucideIcon } from "lucide-react";
|
|
||||||
import type { ButtonAnimationType } from "@/types/button";
|
|
||||||
|
|
||||||
interface Metric {
|
interface Metric {
|
||||||
icon: LucideIcon;
|
icon: LucideIcon;
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AboutMetricProps {
|
interface AboutMetricProps {
|
||||||
title: string;
|
title: string;
|
||||||
metrics: Metric[];
|
metrics: Metric[];
|
||||||
metricsAnimation: ButtonAnimationType;
|
metricsAnimation?: string;
|
||||||
useInvertedBackground: boolean;
|
useInvertedBackground?: boolean;
|
||||||
ariaLabel?: string;
|
|
||||||
className?: string;
|
|
||||||
containerClassName?: string;
|
|
||||||
titleClassName?: string;
|
|
||||||
metricsContainerClassName?: string;
|
|
||||||
metricCardClassName?: string;
|
|
||||||
metricIconClassName?: string;
|
|
||||||
metricLabelClassName?: string;
|
|
||||||
metricValueClassName?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AboutMetric = ({
|
export default function AboutMetric({
|
||||||
title,
|
title,
|
||||||
metrics,
|
metrics,
|
||||||
metricsAnimation,
|
metricsAnimation = 'slide-up',
|
||||||
useInvertedBackground,
|
useInvertedBackground = false,
|
||||||
ariaLabel = "About metrics section",
|
}: AboutMetricProps) {
|
||||||
className = "",
|
const [flipped, setFlipped] = useState<{ [key: number]: boolean }>({});
|
||||||
containerClassName = "",
|
|
||||||
titleClassName = "",
|
|
||||||
metricsContainerClassName = "",
|
|
||||||
metricCardClassName = "",
|
|
||||||
metricIconClassName = "",
|
|
||||||
metricLabelClassName = "",
|
|
||||||
metricValueClassName = "",
|
|
||||||
}: AboutMetricProps) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle);
|
|
||||||
const { containerRef: metricsContainerRef } = useButtonAnimation({ animationType: metricsAnimation });
|
|
||||||
|
|
||||||
const gridColsMap = {
|
const toggleFlip = (index: number) => {
|
||||||
2: "md:grid-cols-2",
|
setFlipped((prev) => ({
|
||||||
3: "md:grid-cols-3",
|
...prev,
|
||||||
4: "md:grid-cols-4",
|
[index]: !prev[index],
|
||||||
};
|
}));
|
||||||
const gridCols = gridColsMap[metrics.length as keyof typeof gridColsMap] || "md:grid-cols-4";
|
};
|
||||||
|
|
||||||
return (
|
const bgClass = useInvertedBackground ? 'bg-slate-900' : 'bg-white';
|
||||||
<section
|
const textClass = useInvertedBackground ? 'text-white' : 'text-slate-900';
|
||||||
aria-label={ariaLabel}
|
|
||||||
className={cls("relative py-20 w-full", useInvertedBackground && "bg-foreground", className)}
|
|
||||||
>
|
|
||||||
<div className={cls("w-content-width mx-auto flex flex-col gap-8", containerClassName)}>
|
|
||||||
<TextAnimation
|
|
||||||
type={theme.defaultTextAnimation}
|
|
||||||
text={title}
|
|
||||||
variant="words-trigger"
|
|
||||||
className={cls("text-2xl md:text-5xl font-medium leading-[1.175]", useInvertedBackground && "text-background", titleClassName)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div ref={metricsContainerRef} className={cls("grid grid-cols-1 gap-6", gridCols, metricsContainerClassName)}>
|
const getAnimationDelay = (index: number) => {
|
||||||
{metrics.map((metric, index) => {
|
if (metricsAnimation === 'slide-up') {
|
||||||
const Icon = metric.icon;
|
return {
|
||||||
return (
|
transitionDelay: `${index * 100}ms`,
|
||||||
<div
|
};
|
||||||
key={index}
|
}
|
||||||
className={cls(
|
return {};
|
||||||
"h-fit card rounded-theme-capped px-6 py-8 md:py-10 flex flex-col items-center justify-center gap-3",
|
};
|
||||||
metricCardClassName
|
|
||||||
)}
|
return (
|
||||||
>
|
<section className={`${bgClass} py-16 px-4 sm:px-6 lg:px-8`}>
|
||||||
<div className="relative z-1 w-full flex items-center justify-center gap-2">
|
<div className="max-w-6xl mx-auto">
|
||||||
<div className={cls("h-8 primary-button aspect-square rounded-theme flex items-center justify-center", metricIconClassName)}>
|
<div className="mb-12">
|
||||||
<Icon className="h-4/10 text-primary-cta-text" strokeWidth={1.5} />
|
<h2 className={`text-3xl sm:text-4xl font-bold ${textClass} text-center`}>
|
||||||
</div>
|
{title}
|
||||||
<h3 className={cls("text-xl truncate", shouldUseLightText && "text-background", metricLabelClassName)}>
|
</h2>
|
||||||
{metric.label}
|
</div>
|
||||||
</h3>
|
|
||||||
</div>
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
<div className="relative z-1 w-full flex items-center justify-center">
|
{metrics.map((metric, index) => {
|
||||||
<h4 className={cls("text-6xl font-medium truncate", shouldUseLightText && "text-background", metricValueClassName)}>
|
const Icon = metric.icon;
|
||||||
{metric.value}
|
const isFlipped = flipped[index] || false;
|
||||||
</h4>
|
|
||||||
</div>
|
return (
|
||||||
</div>
|
<div
|
||||||
);
|
key={index}
|
||||||
})}
|
className="h-64 cursor-pointer perspective"
|
||||||
|
onClick={() => toggleFlip(index)}
|
||||||
|
style={getAnimationDelay(index)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="relative w-full h-full transition-transform duration-500 transform-gpu"
|
||||||
|
style={{
|
||||||
|
transformStyle: 'preserve-3d',
|
||||||
|
transform: isFlipped ? 'rotateY(180deg)' : 'rotateY(0deg)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Front Side */}
|
||||||
|
<div
|
||||||
|
className={`absolute w-full h-full ${bgClass} rounded-lg shadow-lg p-6 flex flex-col items-center justify-center border border-slate-200`}
|
||||||
|
style={{
|
||||||
|
backfaceVisibility: 'hidden',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon className={`w-12 h-12 ${useInvertedBackground ? 'text-blue-400' : 'text-blue-600'} mb-4`} />
|
||||||
|
<p className={`text-sm font-semibold ${textClass} text-center mb-2`}>
|
||||||
|
{metric.label}
|
||||||
|
</p>
|
||||||
|
<p className={`text-3xl font-bold ${useInvertedBackground ? 'text-blue-400' : 'text-blue-600'}`}>
|
||||||
|
{metric.value}
|
||||||
|
</p>
|
||||||
|
<p className={`text-xs ${useInvertedBackground ? 'text-slate-400' : 'text-slate-500'} mt-4 text-center`}>
|
||||||
|
Click to learn more
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Back Side */}
|
||||||
|
<div
|
||||||
|
className={`absolute w-full h-full ${useInvertedBackground ? 'bg-slate-800' : 'bg-slate-100'} rounded-lg shadow-lg p-6 flex flex-col items-center justify-center border border-slate-200`}
|
||||||
|
style={{
|
||||||
|
backfaceVisibility: 'hidden',
|
||||||
|
transform: 'rotateY(180deg)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p className={`text-sm ${textClass} text-center leading-relaxed`}>
|
||||||
|
{metric.label}: {metric.value} represents our commitment to excellence and continuous growth in this area.
|
||||||
|
</p>
|
||||||
|
<p className={`text-xs ${useInvertedBackground ? 'text-slate-400' : 'text-slate-500'} mt-4 text-center`}>
|
||||||
|
Click to flip back
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
);
|
||||||
);
|
})}
|
||||||
};
|
</div>
|
||||||
|
</div>
|
||||||
AboutMetric.displayName = "AboutMetric";
|
</section>
|
||||||
|
);
|
||||||
export default AboutMetric;
|
}
|
||||||
Reference in New Issue
Block a user