-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: scroll based animation * feat: scroll based animation in VerticalStack * fix: handle card click * fix: move sticky section to laptop view * fix: ignore TS line for testing
- Loading branch information
1 parent
80361d8
commit 6e482f6
Showing
7 changed files
with
257 additions
and
112 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.