feat: add mobile-friendly cube descriptions with click interaction
This commit is contained in:
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user