From 3e6edeb6ff9fc4d28210b9ce0eb5b7242f197e6a Mon Sep 17 00:00:00 2001 From: okanisildur Date: Sun, 25 Jul 2021 15:16:32 +0300 Subject: [PATCH 1/2] implement sticky navbar && highlight navbar menu items as scroll feature --- src/components/pages/landing-page.js | 29 ++++- src/components/ui/landing-page-header.js | 14 ++- src/components/ui/landing-page/faq.js | 108 ++++++++-------- .../ui/landing-page/how-it-works.js | 118 +++++++++--------- .../ui/landing-page/problem-and-solution.js | 111 ++++++++-------- .../ui/landing-page/splash-screen.js | 12 +- src/components/ui/menu-items.js | 3 +- src/utils/use-on-viewport.js | 19 +++ 8 files changed, 235 insertions(+), 179 deletions(-) create mode 100644 src/utils/use-on-viewport.js diff --git a/src/components/pages/landing-page.js b/src/components/pages/landing-page.js index 41be0bfd..fdf36281 100644 --- a/src/components/pages/landing-page.js +++ b/src/components/pages/landing-page.js @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useRef } from 'react'; import { Box, Flex } from '@chakra-ui/react'; import { useLocation } from 'react-router-dom'; import { parse } from 'query-string'; @@ -11,9 +11,18 @@ import Faq from '../ui/landing-page/faq'; import DonatePage from '../ui/landing-page/donate-page'; import LandingPageHeader from '../ui/landing-page-header'; import SponsorList from '../ui/landing-page/sponsor-list'; +import useOnViewport from '../../utils/use-on-viewport'; function LandingPage() { const location = useLocation(); + const splashScreenRef = useRef(); + const problemSolutionRef = useRef(); + const howItWorksRef = useRef(); + const faqRef = useRef(); + const isSplashScreenVisible = useOnViewport(splashScreenRef); + const isProblemSolutionVisible = useOnViewport(problemSolutionRef); + const isHowItWorksVisible = useOnViewport(howItWorksRef); + const isFaqVisible = useOnViewport(faqRef); useEffect(() => { if (location.state?.redirectedFromAuth) { @@ -30,25 +39,33 @@ function LandingPage() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + function activeMenuItem() { + if (isSplashScreenVisible) return '#splash-screen'; + if (isProblemSolutionVisible) return '#problem-solution'; + if (isHowItWorksVisible) return '#how-it-works'; + if (isFaqVisible) return '#faq'; + return ''; + } + return ( <> Uçurtma Projesi - + - + - + - + @@ -57,7 +74,7 @@ function LandingPage() { - + ); diff --git a/src/components/ui/landing-page-header.js b/src/components/ui/landing-page-header.js index 390ee7b3..721bff33 100644 --- a/src/components/ui/landing-page-header.js +++ b/src/components/ui/landing-page-header.js @@ -30,7 +30,7 @@ export function SupportButton({ ...otherProps }) { ); } -function LandingPageHeader() { +function LandingPageHeader({ activeMenuItem }) { const { t } = useTranslation('titles'); const menuItems = [ @@ -62,6 +62,12 @@ function LandingPageHeader() { px={{ base: 4, lg: 0 }} justifyContent="space-between" alignItems="center" + position="fixed" + top="0" + right="0" + left="0" + bg="white" + zIndex="2" > - + diff --git a/src/components/ui/landing-page/faq.js b/src/components/ui/landing-page/faq.js index f14412ee..5e9c347c 100644 --- a/src/components/ui/landing-page/faq.js +++ b/src/components/ui/landing-page/faq.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { forwardRef } from 'react'; import { Box, Heading, @@ -16,65 +16,67 @@ import FAQ from './faq.json'; const { studentQuestions, donatorQuestions } = FAQ; const options = ['student', 'supporter']; -function Faq() { +function Faq(props, ref) { const [activeFaq, setActiveFaq] = React.useState('student'); const { t } = useTranslation(['faq', 'titles']); const questionType = activeFaq === 'student' ? studentQuestions : donatorQuestions; return ( - - - {t('titles:FAQ')} - - {options.map(option => ( - - ))} - + + + + {t('titles:FAQ')} + + {options.map(option => ( + + ))} + - - - - - i % 2 === 0)} - /> - i % 2 !== 0)} - /> - - - + + + + + i % 2 === 0)} + /> + i % 2 !== 0)} + /> + + + + ); } -export default Faq; +export default forwardRef(Faq); diff --git a/src/components/ui/landing-page/how-it-works.js b/src/components/ui/landing-page/how-it-works.js index b4eed641..9e56f932 100644 --- a/src/components/ui/landing-page/how-it-works.js +++ b/src/components/ui/landing-page/how-it-works.js @@ -1,10 +1,10 @@ -import React from 'react'; +import React, { forwardRef } from 'react'; import { Heading, Grid, Text, Box, Divider, Image } from '@chakra-ui/react'; import { useTranslation } from 'react-i18next'; import Container from '../container'; import Card from '../card'; -function HowItWorks() { +function HowItWorks(props, ref) { const { t } = useTranslation(['howItWorks', 'titles']); const cards = [ { @@ -25,63 +25,65 @@ function HowItWorks() { ]; return ( - - - {t('titles:How it works')} - {t('Three Steps')} - - - + - {cards.map(card => ( - - - - {card.title} - - - {card.text} - - - ))} - - + + {t('titles:How it works')} + {t('Three Steps')} + + + + {cards.map(card => ( + + + + {card.title} + + + {card.text} + + + ))} + + + ); } -export default HowItWorks; +export default forwardRef(HowItWorks); diff --git a/src/components/ui/landing-page/problem-and-solution.js b/src/components/ui/landing-page/problem-and-solution.js index 68163669..7f7218ba 100644 --- a/src/components/ui/landing-page/problem-and-solution.js +++ b/src/components/ui/landing-page/problem-and-solution.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { forwardRef } from 'react'; import { Shield, Droplet, CloudLightning } from 'react-feather'; import { useTranslation } from 'react-i18next'; import { Box, Flex, Heading, Text, Grid, Image, Icon } from '@chakra-ui/react'; @@ -6,66 +6,71 @@ import MobileMockupTR from '../../assets/mobile-mockup-tr.svg'; import MobileMockupEN from '../../assets/mobile-mockup-en.svg'; import Container from '../container'; -function ProblemSolution() { +function ProblemSolution(props, ref) { const { t, i18n } = useTranslation('problemAndSolution'); const currentLanguage = i18n.language; return ( - - - - {t('New solution with blockchain')} - {t('What was the problem')} - - - - - - {t('Safe')} - - {t('Safe_details')} - - + + + + + {t('New solution with blockchain')} + {t('What was the problem')} + + + + + + {t('Safe')} + + {t('Safe_details')} + + + + + {t('Transparent')} + + {t('Transparent_details')} + + + - - {t('Transparent')} + + {t('Fast')} - {t('Transparent_details')} + {t('Fast_details')} - - - - - {t('Fast')} - - {t('Fast_details')} - - - - - - - + + + + + + + ); } -export default ProblemSolution; +export default forwardRef(ProblemSolution); diff --git a/src/components/ui/landing-page/splash-screen.js b/src/components/ui/landing-page/splash-screen.js index 7bb830a4..1003f216 100644 --- a/src/components/ui/landing-page/splash-screen.js +++ b/src/components/ui/landing-page/splash-screen.js @@ -1,17 +1,17 @@ -import React from 'react'; +import React, { forwardRef } from 'react'; import { Link as RouterLink, useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { Box, Heading, Text, Button, Stack } from '@chakra-ui/react'; +import { Box, Heading, Text, Button, Stack, Center } from '@chakra-ui/react'; import { ReactComponent as LeftKite } from '../../assets/left-kite.svg'; import { ReactComponent as RightKite } from '../../assets/right-kite.svg'; import Container from '../container'; -function SplashScreen() { +function SplashScreen(props, ref) { const { t } = useTranslation(['splashScreen', 'titles']); const navigate = useNavigate(); return ( - <> + - + ); } -export default SplashScreen; +export default forwardRef(SplashScreen); diff --git a/src/components/ui/menu-items.js b/src/components/ui/menu-items.js index 4e16346e..3c3aa375 100644 --- a/src/components/ui/menu-items.js +++ b/src/components/ui/menu-items.js @@ -2,7 +2,7 @@ import React from 'react'; import { Button, Stack } from '@chakra-ui/react'; import { Link } from 'react-router-dom'; -function MenuItems({ isDrawer, items, ...otherProps }) { +function MenuItems({ isDrawer, items, activeMenuItem, ...otherProps }) { const drawerProps = { wrapper: { flexDirection: 'column', @@ -40,6 +40,7 @@ function MenuItems({ isDrawer, items, ...otherProps }) { flexShrink={0} {...(isDrawer && drawerProps.button)} {...navItem.buttonProps} + isActive={navItem.href === activeMenuItem} > {navItem.label} diff --git a/src/utils/use-on-viewport.js b/src/utils/use-on-viewport.js new file mode 100644 index 00000000..5e36f132 --- /dev/null +++ b/src/utils/use-on-viewport.js @@ -0,0 +1,19 @@ +import { useEffect, useState } from 'react'; + +export default function useOnViewport(ref) { + const [isOnViewport, setIsOnViewport] = useState(false); + + const observer = new IntersectionObserver(([entry]) => + setIsOnViewport(entry.isIntersecting) + ); + + useEffect(() => { + observer.observe(ref.current); + // Remove the observer as soon as the component is unmounted + return () => { + observer.disconnect(); + }; + }, [ref]); + + return isOnViewport; +} From 04080a05ac63649b704568583e020e6e55d87869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Okan=20I=C5=9F=C4=B1ldar?= Date: Sun, 25 Jul 2021 15:22:42 +0300 Subject: [PATCH 2/2] delete unnecessary comment --- src/utils/use-on-viewport.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utils/use-on-viewport.js b/src/utils/use-on-viewport.js index 5e36f132..85f1aa9f 100644 --- a/src/utils/use-on-viewport.js +++ b/src/utils/use-on-viewport.js @@ -9,7 +9,6 @@ export default function useOnViewport(ref) { useEffect(() => { observer.observe(ref.current); - // Remove the observer as soon as the component is unmounted return () => { observer.disconnect(); };