Skip to content

Commit

Permalink
Banner - pause/play (#531)
Browse files Browse the repository at this point in the history
* pause/play on Banner

* fix bug with `duration` infinitely changing on pause
  • Loading branch information
rtrembecky authored Dec 15, 2024
1 parent 4eaa47e commit 66315d1
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 58 deletions.
91 changes: 46 additions & 45 deletions src/components/Marquee/Marquee.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,6 @@
import {Box} from '@mui/material'
import {Box, Stack, SxProps, Theme} from '@mui/material'
import {FC, useEffect, useRef, useState} from 'react'

const marqueeSx = {
display: 'flex',
flexDirection: 'row',
marginRight: 'var(--margin-right)',
animation: 'scroll var(--duration) linear var(--delay) infinite',
animationPlayState: 'var(--play)',
animationDelay: 'var(--delay)',
animationDirection: 'var(--direction)',
'@keyframes scroll': {
'0%': {
transform: 'translateX(0)',
},
'100%': {
transform: 'translateX(calc(-100% - var(--margin-right)))',
},
},
}

interface MarqueeProps {
/**
* Inline style for the container div
Expand All @@ -39,11 +21,11 @@ interface MarqueeProps {
*/
pauseOnHover?: boolean
/**
* Whether to pause the marquee when clicked
* Whether to pause the marquee when clicked and held
* Type: boolean
* Default: false
*/
pauseOnClick?: boolean
pauseOnHold?: boolean
/**
* The direction the marquee is sliding
* Type: "left" or "right"
Expand Down Expand Up @@ -86,21 +68,25 @@ interface MarqueeProps {
* Default: null
*/
children?: React.ReactNode
sx?: SxProps<Theme>
onClick?: () => void
}

// tento komponent je z https://github.com/justin-chu/react-fast-marquee/blob/master/src/components/Marquee.tsx
// ale prepisali sme si ho MUI stylmi
export const Marquee: FC<MarqueeProps> = ({
play = true,
pauseOnHover = false,
pauseOnClick = false,
pauseOnHold = false,
direction = 'left',
speed = 20,
delay = 0,
gradient = true,
gradientColor = [255, 255, 255],
gradientWidth = 200,
children,
sx,
onClick,
}) => {
/* React Hooks */
const [containerWidth, setContainerWidth] = useState(0)
Expand Down Expand Up @@ -133,42 +119,57 @@ export const Marquee: FC<MarqueeProps> = ({
const rgbaGradientColor = `rgba(${gradientColor[0]}, ${gradientColor[1]}, ${gradientColor[2]}`

const element = (
<Box
<Stack
ref={marqueeRef}
direction="row"
sx={{
...marqueeSx,
'--play': play ? 'running' : 'paused',
'--direction': direction === 'left' ? 'normal' : 'reverse',
'--duration': `${duration}s`,
'--delay': `${delay}s`,
'--margin-right': `${marqueeWidth < containerWidth ? containerWidth - marqueeWidth : 0}px`,
'@keyframes scroll': {
'0%': {
transform: 'translateX(0)',
},
'100%': {
transform: 'translateX(calc(-100% - var(--margin-right)))',
},
},

// `animation` has to be defined first, next properties adjust its state
animation: 'scroll var(--duration) linear var(--delay) infinite',
animationDelay: 'var(--delay)',
animationDirection: 'var(--direction)',
marginRight: 'var(--margin-right)',

animationPlayState: 'var(--play)',
}}
>
{children}
</Box>
</Stack>
)

return (
<>
{!isMounted ? null : (
<Box
<Stack
ref={containerRef}
direction="row"
onClick={onClick}
sx={{
overflowX: 'hidden !important',
display: 'flex !important',
flexDirection: 'row !important',
overflowX: 'hidden',
position: 'relative',
height: '100%',
width: '100%',
animationPlayState: 'running',
'&:hover div': {
animationPlayState: 'var(--pause-on-hover)',
},
'&:active div': {
animationPlayState: 'var(--pause-on-click)',
},
'--pause-on-hover': pauseOnHover ? 'paused' : 'running',
'--pause-on-click': pauseOnClick ? 'paused' : 'running',
cursor: onClick ? 'pointer' : 'default',

// `toFixed` fixes a weird bug with `duration` infinitely changing on pause
'--duration': `${duration.toFixed(4)}s`,
'--delay': `${delay}s`,
'--direction': direction === 'left' ? 'normal' : 'reverse',
'--margin-right': `${marqueeWidth < containerWidth ? containerWidth - marqueeWidth : 0}px`,

'--play': play ? 'running' : 'paused',
'&:hover': pauseOnHover ? {'--play': 'paused'} : {},
'&:active': pauseOnHold ? {'--play': 'paused'} : {},

...sx,
}}
>
{gradient && (
Expand All @@ -180,7 +181,7 @@ export const Marquee: FC<MarqueeProps> = ({

'&::before, &::after': {
background: 'linear-gradient(to right, var(--gradient-color))',
content: '',
content: '""',
height: '100%',
position: 'absolute',
width: 'var(--gradient-width)',
Expand All @@ -205,7 +206,7 @@ export const Marquee: FC<MarqueeProps> = ({
)}
{element}
{element}
</Box>
</Stack>
)}
</>
)
Expand Down
21 changes: 13 additions & 8 deletions src/components/PageLayout/Banner/Banner.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import {Box, Typography} from '@mui/material'
import {Typography} from '@mui/material'
import {FC} from 'react'

import {Marquee} from '@/components/Marquee/Marquee'
import {BannerAnimationContainer} from '@/utils/BannerAnimationProvider'
import {BannerContainer} from '@/utils/BannerContainer'

export const Banner: FC = () => {
const {bannerMessages} = BannerContainer.useContainer()
const {play, togglePlay} = BannerAnimationContainer.useContainer()

const divider = ' - '

const bannerTextFormatted =
Expand All @@ -16,19 +19,21 @@ export const Banner: FC = () => {
}

return (
<Box
<Marquee
gradient={false}
speed={100}
play={play}
onClick={togglePlay}
sx={{
bgcolor: 'black',
color: 'white',
zIndex: 3,
py: '0.2rem',
}}
>
<Marquee gradient={false} speed={100}>
<Typography variant="h2" component="span" sx={{whiteSpace: 'nowrap'}}>
{bannerTextFormatted}
</Typography>
</Marquee>
</Box>
<Typography variant="h2" component="span" sx={{whiteSpace: 'nowrap'}}>
{bannerTextFormatted}
</Typography>
</Marquee>
)
}
13 changes: 8 additions & 5 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {AlertBox} from '@/components/Alert/AlertBox'
import {theme} from '@/theme'
import {AlertContainer} from '@/utils/AlertContainer'
import {AuthContainer} from '@/utils/AuthContainer'
import {BannerAnimationContainer} from '@/utils/BannerAnimationProvider'
import {useAlert} from '@/utils/useAlert'

const ReactQueryProvider: FC<PropsWithChildren> = ({children}) => {
Expand Down Expand Up @@ -91,11 +92,13 @@ const MyApp: FC<AppProps> = ({Component, pageProps}) => {
<ReactQueryDevtools />
<CookiesProvider>
<AuthContainer.Provider>
<ThemeProvider theme={theme}>
<CssBaseline />
<AlertBox />
<Component {...pageProps} />
</ThemeProvider>
<BannerAnimationContainer.Provider>
<ThemeProvider theme={theme}>
<CssBaseline />
<AlertBox />
<Component {...pageProps} />
</ThemeProvider>
</BannerAnimationContainer.Provider>
</AuthContainer.Provider>
</CookiesProvider>
</ReactQueryProvider>
Expand Down
11 changes: 11 additions & 0 deletions src/utils/BannerAnimationProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {useState} from 'react'
import {createContainer} from 'unstated-next'

const useBannerAnimation = () => {
const [play, setPlay] = useState(true)
const togglePlay = () => setPlay((prev) => !prev)

return {play, setPlay, togglePlay}
}

export const BannerAnimationContainer = createContainer(useBannerAnimation)

0 comments on commit 66315d1

Please sign in to comment.