Skip to content

Commit

Permalink
SOEOPSFY24-301 | Clean up and remove fade animation for timeline over…
Browse files Browse the repository at this point in the history
…view and trapezoid
  • Loading branch information
rebeccahongsf committed Nov 19, 2024
1 parent 01922df commit fb64262
Show file tree
Hide file tree
Showing 45 changed files with 129 additions and 528 deletions.
13 changes: 6 additions & 7 deletions app/components/Timeline/TimelineItem.styles.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
export const size = {
small: "w-150 h-150",
medium: "w-200 h-200",
large: "w-300 h-300",
small: "max-w-150 max-h-150",
medium: "max-w-200 max-h-200",
large: "max-w-300 max-h-300",
full: "w-full h-full",
};

export const trapezoid = {
1: "trapezoid-1",
2: "trapezoid-2",
3: "trapezoid-3",
4: "trapezoid-4",
left: "rotate-y-[25deg] hocus:rotate-y-[-25deg]",
right: "rotate-y-[-25deg] hocus:rotate-y-[25deg]",
};
72 changes: 40 additions & 32 deletions app/components/Timeline/TimelineItem.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,66 @@
import { cnb } from "cnbuilder";
import * as styles from "./TimelineItem.styles";
import * as types from "./TimelineItem.types";
import { XCircleIcon } from "@heroicons/react/24/outline";
import { Heading, Text } from "../Typography";
import { AnimatePresence, MotionProps } from "framer-motion";

interface TimelineItemProps {
heading: string;
year: string;
image: string;
size?: types.SizeType;
trapezoid?: types.TrapezoidType;
className?: string;
onClick?: () => void;
isSelected?: boolean;
animationProps?: MotionProps;
}

const TimelineItem = ({
export const TimelineItem = ({
heading,
year,
image,
size = "medium",
trapezoid = 1,
isSelected,
trapezoid = "left",
className,
animationProps,
...props
}: TimelineItemProps) => {
const imageSize = styles.size[size];
const trapezoidType = styles.trapezoid[trapezoid];

return (
<button
<div
{...props}
className={cnb(
"group relative hocus:transform-none",
trapezoidType,
className,
isSelected && "transform-none",
)}
className={cnb("flex flex-col items-center justify-center", className)}
>
<div className={cnb("relative aspect-[1/1]", imageSize)}>
<img
alt=""
src={image}
className="inset-0 w-full h-full object-cover rounded-lg"
/>
</div>
{isSelected && (
<XCircleIcon
width={50}
className="z-50 absolute flex items-center justify-center text-white group-hocus:text-digital-red"
/>
)}
<p className="z-10 absolute bottom-0 left-0 p-10 rounded bg-black-30">
{year}
</p>
</button>
<AnimatePresence>
<button className={cnb("group relative", trapezoidType)}>
<div
className={cnb(
"aspect-[1/1] relative perspective-600 group-hocus:scale-105 transform ease-in-out duration-1000",
imageSize,
)}
>
<img
alt=""
src={image}
className="inset-0 w-full h-full object-cover rounded-lg transform ease-in-out duration-1000"
/>
</div>
</button>
<div className="flex flex-col *:font-dm-sans">
<Heading className="type-0" weight="normal">
{heading}
</Heading>
<Text
font="serif"
variant="overview"
weight="normal"
className="order-first mt-28"
>
{year}
</Text>
</div>
</AnimatePresence>
</div>
);
};

export default TimelineItem;
117 changes: 46 additions & 71 deletions app/components/Timeline/TimelineOverview.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
"use client";

import { Container } from "@/components/Container";
import TimelineItem from "./TimelineItem";
import { TimelineItem } from "./TimelineItem";
import { SizeType } from "./TimelineItem.types";
import { cnb } from "cnbuilder";
import { useState } from "react";
import { TimelineDetails } from "./TimelineDetails";
import { motion, AnimatePresence } from "framer-motion";
import { SizeType, TrapezoidType } from "./TimelineItem.types";

type TimelineItemData = {
year: string;
Expand All @@ -19,81 +16,59 @@ type TimelineItemData = {

type TimelineProps = {
timelineData: TimelineItemData[];
hasBorder?: boolean;
};

const sizes: SizeType[] = ["small", "medium", "large"];
const trapezoids: TrapezoidType[] = [1, 2, 3, 4];
// Helper function to generate a random delay between a specified range
function getRandomDelay(min = 0.5, max = 5) {
return Math.random() * (max - min) + min;
}

const TimelineOverview = ({ timelineData }: TimelineProps) => {
const [expandedItemIndex, setExpandedItemIndex] = useState<number | null>(
null,
);
// Create rows of items (5 items for odd rows, 4 items for even rows)
const rows: TimelineItemData[][] = [];
let index = 0;

while (index < timelineData.length) {
const rowSize = rows.length % 2 === 0 ? 5 : 4; // Alternate: 5 items for odd rows, 4 items for even rows
rows.push(timelineData.slice(index, index + rowSize));
index += rowSize;
}
// Animation variant with random delay for overlay fade-out
const overlayFadeOutVariant = {
visible: { opacity: 1 },
hidden: (custom: number) => ({
opacity: 0,
transition: {
duration: 1,
delay: custom, // Use the random delay
},
}),
};

const handleExpand = (index: number) => {
setExpandedItemIndex((prevIndex) => (prevIndex === index ? null : index));
};
const TimelineOverview = ({ timelineData, hasBorder }: TimelineProps) => {
const sizePattern: SizeType[] = ["large", "medium", "small"];

return (
<Container width="site" py={5} bgColor="fog-light">
{rows.map((row, rowIndex) => {
const rowSize = row.length; // Determine the size of the current row
<Container width="site" py={5} bgColor="fog-light" className="mb-50">
<div className="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-4">
{timelineData.map((item, idx) => {
const trapezoid = idx % 2 === 0 ? "right" : "left";
const size = sizePattern[idx % sizePattern.length];
const randomDelay = getRandomDelay();

return (
<div key={rowIndex} className="mb-50">
<div
return (
<TimelineItem
key={idx}
{...item}
size={size}
trapezoid={trapezoid}
className={cnb(
"grid gap-50 py-25",
rowSize === 5 ? "grid-cols-5" : "grid-cols-4 px-200",
"rs-pb-3 rs-pt-5 rs-px-4",
hasBorder &&
"border-r-3 border-black nth-2n:border-0 md:nth-2n:border-r-3 md:nth-3n:border-0 xl:nth-3n:border-r-3 xl:nth-4n:border-0",
)}
>
{row.map((item, idx) => {
const itemIndex = rows.slice(0, rowIndex).flat().length + idx;
const isSelected = expandedItemIndex === itemIndex;

return (
<TimelineItem
key={itemIndex}
{...item}
size={sizes[itemIndex % sizes.length]}
trapezoid={trapezoids[itemIndex % trapezoids.length]}
isSelected={isSelected}
className="rounded-lg flex items-center justify-center"
onClick={() => handleExpand(itemIndex)}
/>
);
})}
</div>
{/* Conditionally render the banner if an item in this row is expanded */}
<AnimatePresence>
{expandedItemIndex !== null &&
expandedItemIndex >= rows.slice(0, rowIndex).flat().length &&
expandedItemIndex <
rows.slice(0, rowIndex + 1).flat().length && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: "auto", opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.3, ease: "easeInOut" }}
>
<TimelineDetails
{...timelineData[expandedItemIndex]}
onClose={() => setExpandedItemIndex(null)}
/>
</motion.div>
)}
</AnimatePresence>
</div>
);
})}
animationProps={{
custom: randomDelay,
initial: "visible",
animate: "hidden",
exit: "visible",
variants: overlayFadeOutVariant,
}}
/>
);
})}
</div>
</Container>
);
};
Expand Down
File renamed without changes.
File renamed without changes.
11 changes: 0 additions & 11 deletions app/components/TimelineEven/TimelineDetails.styles.ts

This file was deleted.

117 changes: 0 additions & 117 deletions app/components/TimelineEven/TimelineDetails.tsx

This file was deleted.

Loading

0 comments on commit fb64262

Please sign in to comment.