feat: add mobile-friendly cube descriptions with click interaction

This commit is contained in:
2025-09-19 00:12:16 +02:00
parent a462afc8b2
commit 2b5c502724
2 changed files with 27 additions and 23 deletions

View File

@@ -11,6 +11,7 @@ interface CubeProps {
index: number; index: number;
onHover: () => void; onHover: () => void;
onLeave: () => void; onLeave: () => void;
onClick: () => void;
} }
const CubeSvg: React.FC<React.SVGProps<SVGSVGElement> & { index: number }> = ({ index, ...props }) => ( const CubeSvg: React.FC<React.SVGProps<SVGSVGElement> & { index: number }> = ({ index, ...props }) => (
@@ -42,13 +43,14 @@ const CubeSvg: React.FC<React.SVGProps<SVGSVGElement> & { index: number }> = ({
</svg> </svg>
); );
export function Cube({ title, descriptionTitle, description, isActive, index, onHover, onLeave }: CubeProps) { export function Cube({ title, descriptionTitle, description, isActive, index, onHover, onLeave, onClick }: CubeProps) {
return ( return (
<div className="relative flex flex-col items-center"> <div className="relative flex flex-col items-center">
<motion.div <motion.div
className="relative cursor-pointer" className="relative cursor-pointer"
onMouseEnter={onHover} onMouseEnter={onHover}
onMouseLeave={onLeave} onMouseLeave={onLeave}
onClick={onClick}
style={{ style={{
zIndex: 10 - index, zIndex: 10 - index,
}} }}
@@ -123,25 +125,7 @@ export function Cube({ title, descriptionTitle, description, isActive, index, on
)} )}
{/* Description for Mobile - Below cube */} {/* Description for Mobile - Below cube */}
{isActive && ( </motion.div>
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 10 }}
transition={{ duration: 0.3 }}
className="lg:hidden absolute top-full left-1/2 -translate-x-1/2 mt-8 z-50"
>
<div className="w-64 sm:w-80 px-4">
<h4 className="text-white text-base font-semibold mb-2 text-center">
{descriptionTitle}
</h4>
<p className="text-white text-sm leading-relaxed font-light text-center">
{description}
</p>
</div>
</motion.div>
)}
</motion.div>
</div> </div>
); );
} }

View File

@@ -33,14 +33,22 @@ const stackData = [
export function StackedCubes() { export function StackedCubes() {
const [active, setActive] = useState<string | null>("agent"); const [active, setActive] = useState<string | null>("agent");
const [selectedForMobile, setSelectedForMobile] = useState<string | null>(null);
const handleCubeClick = (id: string) => {
setSelectedForMobile(prev => (prev === id ? null : id));
};
const selectedMobileLayer = stackData.find(layer => layer.id === selectedForMobile);
return ( return (
<div className="flex flex-col items-center">
<div <div
className="relative w-full flex items-center justify-center lg:justify-start min-h-[400px] sm:min-h-[500px] lg:min-h-[400px]" className="relative w-full flex items-center justify-center lg:justify-start min-h-[300px] sm:min-h-[400px] lg:min-h-[400px]"
onMouseLeave={() => setActive("agent")} onMouseLeave={() => setActive("agent")}
> >
<motion.div <motion.div
className="relative ml-0 sm:ml-4 lg:ml-8 h-[400px] w-96" className="relative ml-0 sm:ml-4 lg:ml-8 h-[300px] sm:h-[400px] lg:h-[400px] w-64 sm:w-80 lg:w-96"
animate={{ y: ["-8px", "8px"] }} animate={{ y: ["-8px", "8px"] }}
transition={{ transition={{
duration: 4, duration: 4,
@@ -54,7 +62,7 @@ export function StackedCubes() {
key={layer.id} key={layer.id}
className="absolute" className="absolute"
style={{ style={{
top: `${index * 140}px`, top: `calc(${index * 30}% - ${index * 10}px)`,
zIndex: active === layer.id ? 20 : 10 - index, zIndex: active === layer.id ? 20 : 10 - index,
}} }}
> >
@@ -66,10 +74,22 @@ export function StackedCubes() {
index={index} index={index}
onHover={() => setActive(layer.id)} onHover={() => setActive(layer.id)}
onLeave={() => {}} onLeave={() => {}}
onClick={() => handleCubeClick(layer.id)}
/> />
</div> </div>
))} ))}
</motion.div> </motion.div>
</div> </div>
{selectedMobileLayer && (
<div className="lg:hidden w-full max-w-md p-6 mt-8 bg-gray-800/50 rounded-lg">
<h4 className="text-white text-lg font-semibold mb-2 text-center">
{selectedMobileLayer.descriptionTitle}
</h4>
<p className="text-gray-300 text-sm leading-relaxed text-center">
{selectedMobileLayer.description}
</p>
</div>
)}
</div>
); );
} }