Skip to content

Commit

Permalink
Merge branch 'wallet-landing-page' into wallet-hero-styles
Browse files Browse the repository at this point in the history
  • Loading branch information
DiogoSoaress authored Nov 20, 2024
2 parents 931a617 + 6e482f6 commit 67b80b2
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 112 deletions.
105 changes: 105 additions & 0 deletions src/components/Wallet/VerticalSlide/Table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { type RefObject, useEffect, useState } from 'react'
import { ButtonBase, Grid, Typography } from '@mui/material'
import type { BaseBlock } from '@/components/Home/types'
import BracketLeft from '@/public/images/Wallet/VerticalSlide/bracket-left.svg'
import RecoveryIcon from '@/public/images/Wallet/VerticalSlide/recovery.svg'
import ScanIcon from '@/public/images/Wallet/VerticalSlide/scan.svg'
import MultipleKeysIcon from '@/public/images/Wallet/VerticalSlide/multiple-keys.svg'
import BracketRight from '@/public/images/Wallet/VerticalSlide/bracket-right.svg'
import css from './styles.module.css'
import useScrollProgress from '@/hooks/useScrollProgress'
import { selectIndex } from '@/lib/Wallet/selectIndex'

const icons = [<RecoveryIcon key="recovery" />, <ScanIcon key="scan" />, <MultipleKeysIcon key="multiple-keys" />]

const indexToScrollProgress = (index: number) => {
if (index === 0) {
return 0.1
} else if (index === 1) {
return 0.4
} else {
return 0.5
}
}

const Table = ({ items = [], sectionRef }: { items: BaseBlock['items']; sectionRef: RefObject<HTMLDivElement> }) => {
const [selectedIndex, setSelectedIndex] = useState(0)
const { scrollYProgress } = useScrollProgress(sectionRef)

const itemsImages = items.map((item) => item.image)
const selectedImage = itemsImages[selectedIndex]

const handleCardClick = (index: number) => {
setSelectedIndex(index)

// Move the scroll to the selected index according to indexToScrollProgress function
if (sectionRef.current) {
const sectionTop = sectionRef.current.getBoundingClientRect().top + window.scrollY
const sectionHeight = sectionRef.current.scrollHeight

const offset = indexToScrollProgress(index) * sectionHeight

// @ts-ignore
window.scrollTo({ top: sectionTop + offset, behavior: 'instant' })
}
}

useEffect(() => {
const handleScroll = () => {
const newIndex = selectIndex(scrollYProgress.get())
setSelectedIndex(newIndex)
}

const unsubscribe = scrollYProgress.on('change', handleScroll)

return () => {
unsubscribe()
}
}, [scrollYProgress, items.length])

return (
<Grid container spacing="40px" justifyContent="flex-end">
<Grid item md={7} className={css.imageItem}>
<div className={css.showInMd}>
<BracketLeft />
{icons.map((icon, index) => (
<span key={index} className={index === selectedIndex ? css.selected : undefined}>
{icon}
</span>
))}
<BracketRight />
</div>

{selectedImage ? <img src={selectedImage.src} alt={selectedImage.alt} className={css.image} /> : null}
</Grid>

<Grid item xs={12} md={5}>
<div className={css.cardWrapper}>
{items.map((item, index) => {
const { title, text } = item

return (
<Grid item key={index} xs={12}>
<ButtonBase
onClick={() => handleCardClick(index)}
disableRipple
className={`${css.card} ${index === selectedIndex ? css.selected : ''}`}
>
<Typography variant="h5">{title}</Typography>

{text && (
<Typography color="primary.light" component="div">
{text}
</Typography>
)}
</ButtonBase>
</Grid>
)
})}
</div>
</Grid>
</Grid>
)
}

export default Table
84 changes: 13 additions & 71 deletions src/components/Wallet/VerticalSlide/index.tsx
Original file line number Diff line number Diff line change
@@ -1,82 +1,24 @@
import { useEffect, useState } from 'react'
import { ButtonBase, Container, Grid, Typography } from '@mui/material'
import dynamic from 'next/dynamic'
import { useRef } from 'react'
import { Container, Typography } from '@mui/material'
import type { BaseBlock } from '@/components/Home/types'
import BracketLeft from '@/public/images/Wallet/VerticalSlide/bracket-left.svg'
import RecoveryIcon from '@/public/images/Wallet/VerticalSlide/recovery.svg'
import ScanIcon from '@/public/images/Wallet/VerticalSlide/scan.svg'
import MultipleKeysIcon from '@/public/images/Wallet/VerticalSlide/multiple-keys.svg'
import BracketRight from '@/public/images/Wallet/VerticalSlide/bracket-right.svg'
import layoutCss from '@/components/common/styles.module.css'
import css from './styles.module.css'

const icons = [<RecoveryIcon key="recovery" />, <ScanIcon key="scan" />, <MultipleKeysIcon key="multiple-keys" />]
const Table = dynamic(() => import('./Table'))

const VerticalSlide = ({ title, items = [] }: BaseBlock) => {
const [selectedIndex, setSelectedIndex] = useState(0)

const itemsImages = items.map((item) => item.image)
const selectedImage = itemsImages[selectedIndex]

const handleCardClick = (index: number) => {
setSelectedIndex(index)
}

// Change index every 5 seconds
useEffect(() => {
const interval = setInterval(() => {
setSelectedIndex((prevIndex) => (prevIndex + 1) % items?.length)
}, 5000)

return () => clearInterval(interval) // Cleanup interval on component unmount
}, [items.length])
const sectionRef = useRef<HTMLDivElement>(null)

return (
<Container className={layoutCss.containerMedium}>
<Typography variant="h2" className={css.title}>
{title}
</Typography>

<Grid container spacing="40px" justifyContent="flex-end">
<Grid item md={7} className={css.imageItem}>
<div className={css.showInMd}>
<BracketLeft />
{icons.map((icon, index) => (
<span key={index} className={index === selectedIndex ? css.selected : undefined}>
{icon}
</span>
))}
<BracketRight />
</div>

{selectedImage ? <img src={selectedImage.src} alt={selectedImage.alt} className={css.image} /> : null}
</Grid>

<Grid item xs={12} md={5}>
<div className={css.cardWrapper}>
{items.map((item, index) => {
const { title, text } = item

return (
<Grid item key={index} xs={12}>
<ButtonBase
onClick={() => handleCardClick(index)}
disableRipple
className={`${css.card} ${index === selectedIndex ? css.selected : ''}`}
>
<Typography variant="h5">{title}</Typography>

{text && (
<Typography color="primary.light" component="div">
{text}
</Typography>
)}
</ButtonBase>
</Grid>
)
})}
</div>
</Grid>
</Grid>
<Container className={`${layoutCss.containerMedium} ${css.sectionContainer}`} ref={sectionRef}>
<div className={css.stickyContainer}>
<Typography variant="h2" className={css.title}>
{title}
</Typography>

<Table items={items} sectionRef={sectionRef} />
</div>
</Container>
)
}
Expand Down
15 changes: 15 additions & 0 deletions src/components/Wallet/VerticalSlide/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,21 @@
}

@media (min-width: 900px) {
.sectionContainer {
height: 240vh;
}

.stickyContainer {
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
position: sticky;
top: 0;
overflow: hidden;
}

.title {
margin-bottom: 80px;
}
Expand Down
53 changes: 53 additions & 0 deletions src/components/Wallet/VerticalStack/Table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useEffect, useState, type ReactElement, type RefObject } from 'react'
import { Grid, Typography } from '@mui/material'
import useScrollProgress from '@/hooks/useScrollProgress'
import { selectIndex } from '@/lib/Wallet/selectIndex'
import type { BaseBlock } from '@/components/Home/types'
import css from './styles.module.css'

export const GridItem = ({
image,
title,
text,
isSelected,
}: Partial<BaseBlock> & { isSelected: boolean }): ReactElement => (
<Grid item xs={12} className={`${css.card} ${isSelected ? css.selected : ''}`}>
{image ? <img {...image} /> : null}

<Typography variant="h5">{title}</Typography>

{text && (
<Typography color="primary.light" component="div">
{text}
</Typography>
)}
</Grid>
)

const Table = ({ items = [], sectionRef }: { items: BaseBlock['items']; sectionRef: RefObject<HTMLDivElement> }) => {
const [selectedIndex, setSelectedIndex] = useState(0)
const { scrollYProgress } = useScrollProgress(sectionRef)

useEffect(() => {
const handleScroll = () => {
const newIndex = selectIndex(scrollYProgress.get())
setSelectedIndex(newIndex)
}

const unsubscribe = scrollYProgress.on('change', handleScroll)

return () => {
unsubscribe()
}
}, [scrollYProgress, items.length])

return (
<div className={css.cardWrapper}>
{items.map((item, index) => (
<GridItem key={index} isSelected={index === selectedIndex} {...item} />
))}
</div>
)
}

export default Table
71 changes: 31 additions & 40 deletions src/components/Wallet/VerticalStack/index.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,42 @@
import type { DetailedHTMLProps, ReactElement, SourceHTMLAttributes } from 'react'
import dynamic from 'next/dynamic'
import { useRef, type DetailedHTMLProps, type SourceHTMLAttributes } from 'react'
import { Container, Grid, Typography } from '@mui/material'
import type { BaseBlock } from '@/components/Home/types'
import layoutCss from '@/components/common/styles.module.css'
import css from './styles.module.css'

const Table = dynamic(() => import('./Table'))

type VideoEmbed = {
sources: Array<DetailedHTMLProps<SourceHTMLAttributes<HTMLSourceElement>, HTMLSourceElement>>
}

export const GridItem = ({ image, title, text }: Partial<BaseBlock>): ReactElement => (
<Grid item xs={12} className={css.card}>
{image ? <img {...image} /> : null}

<Typography variant="h5">{title}</Typography>

{text && (
<Typography color="primary.light" component="div">
{text}
</Typography>
)}
</Grid>
)

const VerticalStack = ({ title, video, items = [] }: BaseBlock & { video: VideoEmbed }) => (
<Container className={layoutCss.containerShort}>
<Grid container spacing="40px" justifyContent="space-between">
<Grid item md={6} className={css.titleWrapper}>
{video && (
<video autoPlay muted playsInline loop className={css.video}>
{video.sources.map((s, i) => (
<source key={i} {...s} />
))}
</video>
)}

<Typography variant="h2">{title}</Typography>
</Grid>

<Grid item xs={12} md={5}>
<div className={css.cardWrapper}>
{items.map((item, index) => (
<GridItem key={index} {...item} />
))}
</div>
</Grid>
</Grid>
</Container>
)
const VerticalStack = ({ title, video, items = [] }: BaseBlock & { video: VideoEmbed }) => {
const sectionRef = useRef<HTMLDivElement>(null)

return (
<Container className={`${layoutCss.containerShort} ${css.sectionContainer}`} ref={sectionRef}>
<div className={css.stickyContainer}>
<Grid container spacing="40px" justifyContent="space-between">
<Grid item md={6} className={css.titleWrapper}>
{video && (
<video autoPlay muted playsInline loop className={css.video}>
{video.sources.map((s, i) => (
<source key={i} {...s} />
))}
</video>
)}

<Typography variant="h2">{title}</Typography>
</Grid>

<Grid item xs={12} md={6}>
<Table items={items} sectionRef={sectionRef} />
</Grid>
</Grid>
</div>
</Container>
)
}

export default VerticalStack
Loading

0 comments on commit 67b80b2

Please sign in to comment.