diff --git a/package-lock.json b/package-lock.json
index edffa32..130e796 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,6 +14,9 @@
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"@next/third-parties": "^14.1.0",
+ "embla-carousel-auto-scroll": "^8.0.0",
+ "embla-carousel-autoplay": "^8.0.0",
+ "embla-carousel-react": "^8.0.0",
"html-react-parser": "^5.1.2",
"next": "^14.1.0",
"react": "^18",
@@ -1526,6 +1529,47 @@
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
+ "node_modules/embla-carousel": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.0.0.tgz",
+ "integrity": "sha512-ecixcyqS6oKD2nh5Nj5MObcgoSILWNI/GtBxkidn5ytFaCCmwVHo2SecksaQZHcARMMpIR2dWOlSIdA1LkZFUA=="
+ },
+ "node_modules/embla-carousel-auto-scroll": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/embla-carousel-auto-scroll/-/embla-carousel-auto-scroll-8.0.0.tgz",
+ "integrity": "sha512-+bG79hg/BhI4enRevR6fuQdrJevfu3gfNg/S+1kGszXHUJHOOwDQne/dXOnQ12+o74XvDD5t/LG45rAOplehhQ==",
+ "peerDependencies": {
+ "embla-carousel": "8.0.0"
+ }
+ },
+ "node_modules/embla-carousel-autoplay": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/embla-carousel-autoplay/-/embla-carousel-autoplay-8.0.0.tgz",
+ "integrity": "sha512-FWHhZULH5+ydg7fiabwQppCDoTMi8pbMC20lmVytoXn7hH2KAhXHc/8yCUb3yToqMduCN6xPKUONtgzBqz3RZg==",
+ "peerDependencies": {
+ "embla-carousel": "8.0.0"
+ }
+ },
+ "node_modules/embla-carousel-react": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.0.0.tgz",
+ "integrity": "sha512-qT0dii8ZwoCtEIBE6ogjqU2+5IwnGfdt2teKjCzW88JRErflhlCpz8KjWnW8xoRZOP8g0clRtsMEFoAgS/elfA==",
+ "dependencies": {
+ "embla-carousel": "8.0.0",
+ "embla-carousel-reactive-utils": "8.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.1 || ^18.0.0"
+ }
+ },
+ "node_modules/embla-carousel-reactive-utils": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.0.0.tgz",
+ "integrity": "sha512-JCw0CqCXI7tbHDRogBb9PoeMLyjEC1vpN0lDOzUjmlfVgtfF+ffLaOK8bVtXVUEbNs/3guGe3NSzA5J5aYzLzw==",
+ "peerDependencies": {
+ "embla-carousel": "8.0.0"
+ }
+ },
"node_modules/emoji-regex": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
diff --git a/package.json b/package.json
index e08dbfc..889cc12 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,9 @@
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"@next/third-parties": "^14.1.0",
+ "embla-carousel-auto-scroll": "^8.0.0",
+ "embla-carousel-autoplay": "^8.0.0",
+ "embla-carousel-react": "^8.0.0",
"html-react-parser": "^5.1.2",
"next": "^14.1.0",
"react": "^18",
diff --git a/src/app/components/ThroughTheYears.tsx b/src/app/components/ThroughTheYears.tsx
deleted file mode 100644
index 025c6c2..0000000
--- a/src/app/components/ThroughTheYears.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-export default function ThroughTheYears() {
- return (
-
- {" "}
- Coming soon...{" "}
-
- );
-}
diff --git a/src/app/components/ThroughTheYears/EmblaCarousel.tsx b/src/app/components/ThroughTheYears/EmblaCarousel.tsx
new file mode 100644
index 0000000..737ca0f
--- /dev/null
+++ b/src/app/components/ThroughTheYears/EmblaCarousel.tsx
@@ -0,0 +1,231 @@
+import { React, useState, useEffect, useCallback } from "react";
+import { EmblaOptionsType } from "embla-carousel";
+import { DotButton, useDotButton } from "./EmblaCarouselDotButton";
+import useEmblaCarousel from "embla-carousel-react";
+import Image from "next/image";
+import Link from "next/link";
+import AutoScroll from "embla-carousel-auto-scroll";
+
+import styled from "styled-components";
+
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faPlay, faStop } from "@fortawesome/free-solid-svg-icons";
+
+type PropType = {
+ slides: number[];
+ options?: EmblaOptionsType;
+};
+
+const Embla = styled.section`
+ margin: auto;
+ max-width: 100%;
+ --slide-spacing: 1rem;
+ --slide-size: 100%;
+ --slide-spacing-sm: 10rem;
+ --slide-size-sm: 20%;
+ --slide-spacing-lg: 2rem;
+ --slide-size-lg: calc(100% / 4);
+`;
+
+const EmblaViewport = styled.div`
+ overflow: hidden;
+`;
+
+const EmblaContainer = styled.div`
+ backface-visibility: hidden;
+ display: flex;
+ touch-action: pan-y;
+ margin-left: calc(var(--slide-spacing) * -1);
+
+ @media (min-width: 300px) {
+ margin-left: calc(var(--slide-spacing-sm) * -1);
+ }
+
+ @media (min-width: 750px) {
+ margin-left: calc(var(--slide-spacing-lg) * -1);
+ }
+`;
+
+const EmblaSlide = styled.div`
+ min-width: 0;
+ flex: 0 0 var(--slide-size);
+ padding-left: var(--slide-spacing);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ text-align: center;
+
+ @media (min-width: 300px) {
+ flex: 0 0 var(--slide-size-sm);
+ padding-left: var(--slide-spacing-sm);
+ }
+
+ @media (min-width: 750px) {
+ padding-left: var(--slide-spacing-sm);
+ }
+
+ @media (min-width: 1200px) {
+ flex: 0 0 var(--slide-size-lg);
+ padding-left: var(--slide-spacing-lg);
+ }
+`;
+
+const EmblaSlideNumber = styled.div`
+ border-radius: 1.8rem;
+ font-size: 4rem;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: var(--slide-height);
+`;
+
+const EmblaControls = styled.div`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-top: 1.8rem;
+
+ @media (max-width: 767px) {
+ display: none;
+ }
+`;
+
+const EmblaButtons = styled.div`
+ display: flex;
+ gap: 0.6rem;
+ align-items: center;
+`;
+
+const EmblaButton = styled.button`
+ -webkit-appearance: none;
+ appearance: none;
+ background-color: transparent;
+ touch-action: manipulation;
+ display: inline-flex;
+ text-decoration: none;
+ cursor: pointer;
+ border: 0;
+ padding: 0;
+ margin: 0;
+ width: 3.6rem;
+ height: 3.6rem;
+ z-index: 1;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+`;
+
+const EmblaButtonSvg = styled.svg`
+ width: 35%;
+ height: 35%;
+`;
+
+const EmblaDots = styled.div`
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ align-items: center;
+`;
+
+const EmblaPlay = styled.button`
+ -webkit-appearance: none;
+ text-decoration: none;
+ cursor: url("/rocket-fire.png"), auto;
+ display: flex;
+ font-size: 1.5rem;
+ background-color: black;
+ border: none;
+ margin: 0px 0px 0px 10px;
+`;
+
+const EmblaCarousel: React.FC = (props) => {
+ const { slides, options } = props;
+ const [emblaRef, emblaApi] = useEmblaCarousel(options, [
+ AutoScroll({ playOnInit: true }),
+ ]);
+ const [isPlaying, setIsPlaying] = useState(false);
+
+ const { selectedIndex, scrollSnaps, onDotButtonClick } =
+ useDotButton(emblaApi);
+
+ const onButtonAutoplayClick = useCallback(
+ (callback: () => void) => {
+ const autoScroll = emblaApi?.plugins()?.autoScroll;
+ if (!autoScroll) return;
+
+ const resetOrStop =
+ autoScroll.options.stopOnInteraction === false
+ ? autoScroll.reset
+ : autoScroll.stop;
+
+ resetOrStop();
+ callback();
+ },
+ [emblaApi],
+ );
+
+ const toggleAutoplay = useCallback(() => {
+ const autoScroll = emblaApi?.plugins()?.autoScroll;
+ if (!autoScroll) return;
+
+ const playOrStop = autoScroll.isPlaying()
+ ? autoScroll.stop
+ : autoScroll.play;
+ playOrStop();
+ }, [emblaApi]);
+
+ useEffect(() => {
+ const autoScroll = emblaApi?.plugins()?.autoScroll;
+ if (!autoScroll) return;
+
+ setIsPlaying(autoScroll.isPlaying());
+ emblaApi
+ .on("autoScroll:play", () => setIsPlaying(true))
+ .on("autoScroll:stop", () => setIsPlaying(false))
+ .on("reInit", () => setIsPlaying(false));
+ }, [emblaApi]);
+
+ return (
+
+
+
+ {slides.map((edition, index, currentIndex) => (
+
+
+
+ {edition.name}
+ {edition.date}
+
+
+ ))}
+
+
+
+
+ {scrollSnaps.map((_, index) => (
+ onDotButtonClick(index)}
+ selected={selectedIndex === index}
+ />
+ ))}
+
+
+
+
+
+
+ );
+};
+
+export default EmblaCarousel;
diff --git a/src/app/components/ThroughTheYears/EmblaCarouselDotButton.tsx b/src/app/components/ThroughTheYears/EmblaCarouselDotButton.tsx
new file mode 100644
index 0000000..e00b8ba
--- /dev/null
+++ b/src/app/components/ThroughTheYears/EmblaCarouselDotButton.tsx
@@ -0,0 +1,106 @@
+import React, {
+ PropsWithChildren,
+ useCallback,
+ useEffect,
+ useState,
+} from "react";
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { EmblaCarouselType } from "embla-carousel";
+import styled from "styled-components";
+
+type UseDotButtonType = {
+ selectedIndex: number;
+ scrollSnaps: number[];
+ onDotButtonClick: (index: number) => void;
+};
+
+export const useDotButton = (
+ emblaApi: EmblaCarouselType | undefined,
+): UseDotButtonType => {
+ const [selectedIndex, setSelectedIndex] = useState(0);
+ const [scrollSnaps, setScrollSnaps] = useState([]);
+
+ const onDotButtonClick = useCallback(
+ (index: number) => {
+ if (!emblaApi) return;
+ emblaApi.scrollTo(index);
+ },
+ [emblaApi],
+ );
+
+ const onInit = useCallback((emblaApi: EmblaCarouselType) => {
+ setScrollSnaps(emblaApi.scrollSnapList());
+ }, []);
+
+ const onSelect = useCallback((emblaApi: EmblaCarouselType) => {
+ setSelectedIndex(emblaApi.selectedScrollSnap());
+ }, []);
+
+ useEffect(() => {
+ if (!emblaApi) return;
+
+ onInit(emblaApi);
+ onSelect(emblaApi);
+ emblaApi.on("reInit", onInit);
+ emblaApi.on("reInit", onSelect);
+ emblaApi.on("select", onSelect);
+ }, [emblaApi, onInit, onSelect]);
+
+ return {
+ selectedIndex,
+ scrollSnaps,
+ onDotButtonClick,
+ };
+};
+
+type PropType = PropsWithChildren<
+ React.DetailedHTMLProps<
+ React.ButtonHTMLAttributes,
+ HTMLButtonElement
+ >
+>;
+
+const EmblaDot = styled.button`
+ appearance: none;
+ touch-action: manipulation;
+ display: inline-flex;
+ text-decoration: none;
+ cursor: url("/rocket-fire.png"), auto;
+ border: 0;
+ padding: 0;
+ margin: 0.1rem;
+ width: 1rem;
+ height: 1rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 50%;
+ position: relative;
+ background-color: ${(props) =>
+ props.selected
+ ? "orangered"
+ : "slategray"}; // Change 'blue' to your desired color
+
+
+ &::after {
+ width: 1rem;
+ height: 1rem;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ content: "";
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+`;
+
+export const DotButton: React.FC = (props) => {
+ const { children, ...restProps } = props;
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/src/app/components/ThroughTheYears/ThroughTheYears.tsx b/src/app/components/ThroughTheYears/ThroughTheYears.tsx
new file mode 100644
index 0000000..9200815
--- /dev/null
+++ b/src/app/components/ThroughTheYears/ThroughTheYears.tsx
@@ -0,0 +1,88 @@
+import React from "react";
+
+import styled, { keyframes } from "styled-components";
+import { Section } from "@/app/genericComponents/General";
+import { SectionTitle } from "@/app/genericComponents/Typography";
+import useEmblaCarousel from "embla-carousel-react";
+import EmblaCarousel from "./EmblaCarousel";
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { EmblaOptionsType } from "embla-carousel";
+
+export default function ThroughTheYears() {
+ const PREVIOUS_EDITIONS = [
+ {
+ name: "Fall 2016",
+ date: "October 23-25",
+ img: "/Planets/planetPartner.svg",
+ url: "",
+ },
+ {
+ name: "Winter 2016",
+ date: "May 8-12",
+ img: "/Planets/planetSponsor.svg",
+ url: "",
+ },
+ {
+ name: "Fall 2017",
+ date: "May 3-5",
+ img: "/Planets/planetMentor.svg",
+ url: "",
+ },
+ {
+ name: "Winter 2017",
+ date: "May 3-5",
+ img: "/Planets/planetVolunteer.svg",
+ url: "",
+ },
+ {
+ name: "2018",
+ date: "May 3-5",
+ img: "/Planets/planetPartner.svg",
+ url: "https://2018.hackupc.com/",
+ },
+ {
+ name: "2019",
+ date: "May 3-5",
+ img: "/Planets/planetSponsor.svg",
+ url: "https://2019.hackupc.com/",
+ },
+ {
+ name: "2020",
+ date: "CANCELLED",
+ img: "/Planets/planetMentor.svg",
+ url: "",
+ },
+ {
+ name: "2021",
+ date: "May 3-5",
+ img: "/Planets/planetVolunteer.svg",
+ url: "https://2021.hackupc.com/",
+ },
+ {
+ name: "2022",
+ date: "May 3-5",
+ img: "/Planets/planetVolunteer.svg",
+ url: "https://2022.hackupc.com/",
+ },
+ {
+ name: "2023",
+ date: "May 3-5",
+ img: "/Planets/planetVolunteer.svg",
+ url: "https://2023.hackupc.com/",
+ },
+ ];
+
+ const [emblaRef] = useEmblaCarousel();
+ const OPTIONS: EmblaOptionsType = {
+ align: "start",
+ loop: true,
+ slidesToScroll: 2,
+ };
+
+ return (
+
+ );
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 1e7df11..a130cac 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -10,11 +10,10 @@ import FAQs from "@/app/components/FAQs";
import Socials from "@/app/components/Socials";
import SponsorsAndPartners from "@/app/components/SponsorsAndPartners";
import Hackers from "@/app/components/Hackers";
-// TODO: import ThroughTheYears from "@/app/components/ThroughTheYears";
import Hero from "./components/Hero";
import Header from "@/app/components/Header";
import Footer from "@/app/components/Footer";
-import ThroughTheYears from "@/app/components/ThroughTheYears";
+import ThroughTheYears from "@/app/components/ThroughTheYears/ThroughTheYears";
import { Background } from "@/app/genericComponents/General";
export default function Home() {