diff --git a/src/ui/design-system/src/lib/Components/HomeHeader/index.tsx b/src/ui/design-system/src/lib/Components/HomeHeader/index.tsx index 3420828ea1..e800af8409 100644 --- a/src/ui/design-system/src/lib/Components/HomeHeader/index.tsx +++ b/src/ui/design-system/src/lib/Components/HomeHeader/index.tsx @@ -7,12 +7,13 @@ export const HomeHeader: React.FC = () => {
-
+
-
+ {/* TODO: Add back in after iteration */} + {/*
-
+
*/}
diff --git a/src/ui/design-system/src/lib/Components/NewsCarousel/index.tsx b/src/ui/design-system/src/lib/Components/NewsCarousel/index.tsx index 19675cbd46..9340798e13 100644 --- a/src/ui/design-system/src/lib/Components/NewsCarousel/index.tsx +++ b/src/ui/design-system/src/lib/Components/NewsCarousel/index.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import ActionCard from '@site/src/components/ActionCard'; interface CarouselCard { @@ -13,62 +13,181 @@ interface CarouselCard { const CAROUSEL_CARDS: CarouselCard[] = [ { - heading: 'We got a castle!', - description: 'Apply to be a Flow Scholar to work alongside our team, join us at the Flow Hacker Castle, and represent the Flow community—all on us!', + heading: 'Get the newsletter!', + description: 'We send out a newsletter every week with the latest news for Flow developers. Sign up to get it in your inbox!', iconColor: 'green', cardColor: 'black', - href: 'https://x.com/flow_blockchain/status/1880405924407587173', + href: 'https://e.customeriomail.com/manage_subscription_preferences/dgTdoQoDAI7HAY3HAQGWxJT9wbM-Af61EgxuM6E=', variant: 'horizontal' as const, - // icon: 'flow-castle' + icon: 'learn' }, { - heading: 'Cadence Tutorials', - description: 'We\'ve updated the first set of Cadence tutorials to be more approachable. Let us know what you think!', - iconColor: 'blue', + heading: 'May the Flow be with You!', + description: 'We are wrapping up our month-long vibe coding challenge on Flow! Check out the winners, or submit for the last week!', + iconColor: 'green', cardColor: 'black', - href: 'https://cadence-lang.org/docs/tutorial/first-steps', + href: '/ecosystem/Hackathons%20and%20Events/may-the-flow-be-with-you', variant: 'horizontal' as const, - icon: 'tutorials' + icon: 'vcs-&-funds' }, { - heading: 'Flow is Live on Axelar', - description: '"With this integration, Flow users, builders and dApps get access to best-in-class interoperability, opening up liquidity from 70+ chains 🦾 🤝"', + heading: 'ETH Global Prague', + description: 'Earn up to $10,000 in prizes for building on Flow at one of the biggest hackathons in Europe!', iconColor: 'purple', cardColor: 'black', - href: 'https://x.com/axelar/status/1882066175175360998', + href: 'https://ethglobal.com/events/prague/prizes/flow', + variant: 'horizontal' as const, + icon: 'grants' + }, + { + heading: 'EthCC Cannes', + description: 'Earn up to $20,000 in prizes at the largest annual Ethereum conference in Europe!', + iconColor: 'blue', + cardColor: 'black', + href: 'https://ethglobal.com/events/cannes/prizes/flow', variant: 'horizontal' as const, - icon: 'cross-vm-bridge' + icon: 'grants' }, ]; export const NewsCarousel: React.FC = () => { - const [currentIndex, setCurrentIndex] = useState(0); + const [activeIndex, setActiveIndex] = useState(0); + const [isTransitioning, setIsTransitioning] = useState(false); + const [cardsToShow, setCardsToShow] = useState(1); + const totalCards = CAROUSEL_CARDS.length; + // Calculate maxIndex properly to prevent empty segments + const maxIndex = Math.max(0, totalCards - cardsToShow); + + // Initialize cardsToShow based on screen width + useEffect(() => { + const updateCardsToShow = () => { + setCardsToShow(window.innerWidth >= 1024 ? 2 : 1); + }; + + updateCardsToShow(); + window.addEventListener('resize', updateCardsToShow); + return () => window.removeEventListener('resize', updateCardsToShow); + }, []); + + // Handle automatic sliding + useEffect(() => { + const timer = setInterval(() => { + if (!isTransitioning) { + handleNext(); + } + }, 5000); // Auto slide every 5 seconds - const nextSlide = () => { - setCurrentIndex((prevIndex) => (prevIndex + 1) % CAROUSEL_CARDS.length); - }; + return () => clearInterval(timer); + }, [activeIndex, isTransitioning]); - const prevSlide = () => { - setCurrentIndex((prevIndex) => - prevIndex - 1 < 0 ? CAROUSEL_CARDS.length - 1 : prevIndex - 1 - ); - }; + const handleNext = useCallback(() => { + if (isTransitioning) return; + + setIsTransitioning(true); + + // Loop back to beginning if at the end + if (activeIndex >= maxIndex) { + setActiveIndex(0); + } else { + setActiveIndex(prev => prev + 1); + } + + setTimeout(() => { + setIsTransitioning(false); + }, 600); + }, [activeIndex, isTransitioning, maxIndex]); - const getVisibleCards = () => { - if (CAROUSEL_CARDS.length < 2) return [CAROUSEL_CARDS[0]]; + const handlePrev = useCallback(() => { + if (isTransitioning) return; + + setIsTransitioning(true); + + // Loop to the end if at the beginning + if (activeIndex <= 0) { + setActiveIndex(maxIndex); + } else { + setActiveIndex(prev => prev - 1); + } + + setTimeout(() => { + setIsTransitioning(false); + }, 600); + }, [activeIndex, isTransitioning, maxIndex]); - const secondIndex = (currentIndex + 1) % CAROUSEL_CARDS.length; - return [CAROUSEL_CARDS[currentIndex], CAROUSEL_CARDS[secondIndex]]; - }; + const handleDotClick = useCallback((index: number) => { + if (isTransitioning || index === activeIndex) return; + + setIsTransitioning(true); + setActiveIndex(index); + + setTimeout(() => { + setIsTransitioning(false); + }, 600); + }, [activeIndex, isTransitioning]); return ( -
+
+ {/* Card Container */} +
+
+ {CAROUSEL_CARDS.map((card, index) => ( +
+ { + if (card.href.startsWith('https://')) { + window.open(card.href, '_blank'); + } else { + window.location.href = card.href; + } + }} + /> +
+ ))} +
+
+ {/* Navigation Buttons */} -
+
+ +
+ + {/* Mobile navigation buttons */} +
-
- - {/* Cards Grid */} -
- {getVisibleCards().map((card, index) => ( -
- { - if (card.href.startsWith('https://')) { - window.open(card.href, '_blank'); - } else { - window.location.href = card.href; - } - }} + + {/* Dots indicator for mobile */} +
+ {Array.from({ length: maxIndex + 1 }).map((_, index) => ( +
+ ))} +
+ + +
+ + {/* Desktop progress indicator */} +
+ {Array.from({ length: maxIndex + 1 }).map((_, index) => ( +
diff --git a/src/ui/design-system/src/lib/Pages/HomePage/index.tsx b/src/ui/design-system/src/lib/Pages/HomePage/index.tsx index c1d1eda4dd..84de77b09c 100644 --- a/src/ui/design-system/src/lib/Pages/HomePage/index.tsx +++ b/src/ui/design-system/src/lib/Pages/HomePage/index.tsx @@ -10,7 +10,7 @@ import PageBackground from '../shared/PageBackground'; import ActionCardGrid from '@site/src/components/ActionCardGrid'; import { buildGridData } from './GridData/BuildGridData'; import { growGridData } from './GridData/GrowGridData'; -// import { HomeHeader } from '../../Components/HomeHeader'; +import { HomeHeader } from '../../Components/HomeHeader'; export type HomePageProps = SocialLinksSignupProps & { concepts?: TutorialCardProps[]; @@ -21,8 +21,7 @@ export type HomePageProps = SocialLinksSignupProps & { const HomePage = ({ discordUrl, githubUrl }: HomePageProps): JSX.Element => { return ( - {/* TODO: Add back in after iteration */} - {/* */} +