-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[B2BP-753] - Implementing 'Video' EC inside B2BP (#350)
* commit add storeButtons to Hero * commit changeset * commit review changes * commit review changes * commit package-lock * commit file test fix * commit video component * commit changeset * commit video example * commit color fix * commit update video component * commit video variation * Commit review changes * commit review changes * Update smooth-balloons-compare.md * commit review changes * commit implement video functions * Commit review changes * commit review changes
- Loading branch information
1 parent
7a1db57
commit 66789c1
Showing
12 changed files
with
593 additions
and
1 deletion.
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,5 @@ | ||
--- | ||
"nextjs-website": minor | ||
--- | ||
|
||
Implement Video-Image from EC to B2BP |
145 changes: 145 additions & 0 deletions
145
apps/nextjs-website/react-components/components/VideoImage/VideoImage.helpers.tsx
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,145 @@ | ||
import { Typography } from '@mui/material'; | ||
import { isJSX } from '@react-components/types/common/Common.types'; | ||
import { | ||
VideoCaptionProps, | ||
VideoTextProps, | ||
RenderVideoProps, | ||
ImageSrc, | ||
ImageSrcObject, | ||
ImageProps, | ||
} from '@react-components/types/VideoImage/VideoImage.types'; | ||
import { TextColor } from '../common/Common.helpers'; | ||
import { useTheme } from '@mui/material/styles'; | ||
import Image from 'next/image'; | ||
|
||
export const renderVideo = ({ | ||
videoRef, | ||
error, | ||
setError, | ||
src, | ||
loop, | ||
autoplay, | ||
fallback, | ||
onVideoEnd, | ||
}: RenderVideoProps) => { | ||
// Determine the type based on the structure of src | ||
const type = | ||
typeof src === 'string' | ||
? src.split('.').pop() ?? 'mp4' | ||
: src?.mime.split('/').pop() ?? 'mp4'; | ||
|
||
if (error) { | ||
return isJSX(fallback) ? ( | ||
fallback | ||
) : ( | ||
<Typography variant='h6' color='background.paper' textAlign='center'> | ||
{fallback} | ||
</Typography> | ||
); | ||
} | ||
return ( | ||
<video | ||
onContextMenu={(e) => e.preventDefault()} | ||
playsInline | ||
ref={videoRef} | ||
muted | ||
loop={loop} | ||
autoPlay={autoplay} | ||
onEnded={onVideoEnd} | ||
style={{ | ||
overflow: 'hidden', | ||
width: '100%', | ||
height: '100%', | ||
}} | ||
> | ||
<source | ||
src={typeof src === 'object' ? src.url : src} | ||
type={`video/${type}`} | ||
onError={() => setError(true)} | ||
/> | ||
</video> | ||
); | ||
}; | ||
|
||
// Type guard function to check if src is an object | ||
function isImageSrcObject(src: ImageSrc): src is ImageSrcObject { | ||
return typeof src === 'object' && src !== null && 'url' in src; | ||
} | ||
|
||
// Refactored renderImage function | ||
export const renderImage = ({ alt = 'image alt', src }: ImageProps) => { | ||
let imageUrl = isImageSrcObject(src) ? src.url : src; | ||
|
||
if (!imageUrl) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<Image | ||
alt={alt} | ||
src={imageUrl} | ||
width={0} | ||
height={0} | ||
style={{ | ||
objectFit: 'contain', | ||
width: '100%', | ||
height: '100%', | ||
userSelect: 'none', | ||
overflow: 'hidden', | ||
}} | ||
/> | ||
); | ||
}; | ||
|
||
export const VideoText = ({ | ||
title, | ||
subtitle, | ||
theme = 'dark', | ||
}: VideoTextProps) => { | ||
const textColor = TextColor(theme); | ||
return ( | ||
<> | ||
{title && ( | ||
<Typography variant='h5' mb={4} color={textColor}> | ||
{title} | ||
</Typography> | ||
)} | ||
{subtitle && ( | ||
<Typography | ||
paragraph | ||
sx={{ fontSize: '16px' }} | ||
mb={3} | ||
color={textColor} | ||
> | ||
{subtitle} | ||
</Typography> | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
export const VideoCaption = ({ caption, toBeCentered }: VideoCaptionProps) => { | ||
const { palette } = useTheme(); | ||
return ( | ||
<div | ||
style={{ | ||
height: '116px', | ||
marginLeft: toBeCentered ? '0' : '7em', | ||
marginRight: toBeCentered ? '0' : '7em', | ||
marginTop: '1em', | ||
textAlign: toBeCentered ? 'center' : 'left', | ||
}} | ||
> | ||
{caption && ( | ||
<Typography | ||
paragraph | ||
sx={{ fontSize: '16px', fontWeight: 600 }} | ||
mb={3} | ||
color={palette.text.primary} | ||
> | ||
{caption} | ||
</Typography> | ||
)} | ||
</div> | ||
); | ||
}; |
220 changes: 220 additions & 0 deletions
220
apps/nextjs-website/react-components/components/VideoImage/VideoImage.tsx
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,220 @@ | ||
import { useEffect, useRef, useState } from 'react'; | ||
import { useIsVisible } from '@react-components/types/common/Common.types'; | ||
import { VideoImageProps } from '@react-components/types'; | ||
import { TextColor } from '../common/Common.helpers'; | ||
import PlayArrowIcon from '@mui/icons-material/PlayArrow'; | ||
import { | ||
renderImage, | ||
renderVideo, | ||
VideoCaption, | ||
VideoText, | ||
} from './VideoImage.helpers'; | ||
|
||
const VideoImage = (props: VideoImageProps) => { | ||
const { | ||
title, | ||
subtitle, | ||
caption, | ||
src, | ||
alt, | ||
autoplay = false, | ||
loop = false, | ||
full = false, | ||
theme, | ||
fallback, | ||
playButtonLabel, | ||
isCentered = false, | ||
} = props; | ||
const videoRef = useRef<HTMLVideoElement>(null); | ||
const isVisible = useIsVisible(videoRef); | ||
const [error, setError] = useState(false); | ||
const [isPlaying, setIsPlaying] = useState(false); | ||
|
||
const textColor = TextColor(theme); | ||
|
||
useEffect(() => { | ||
if (!isVisible) return; | ||
const startVideoWhenVisible = async () => { | ||
if (autoplay && isVisible) play(); | ||
}; | ||
startVideoWhenVisible().catch(); | ||
}, [isVisible]); | ||
|
||
const play = (e?: React.MouseEvent) => { | ||
try { | ||
e?.preventDefault(); | ||
videoRef.current?.play(); | ||
setIsPlaying(true); | ||
} catch (error) {} | ||
}; | ||
|
||
const handleVideoEnd = () => { | ||
setIsPlaying(false); | ||
}; | ||
|
||
const isImage = (src: string) => { | ||
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']; | ||
return imageExtensions.some((extension) => src.endsWith(extension)); | ||
}; | ||
|
||
const isVideo = (src: string) => { | ||
const videoExtensions = ['.mp4', '.webm', '.ogg']; | ||
return videoExtensions.some((extension) => src.endsWith(extension)); | ||
}; | ||
|
||
return ( | ||
<> | ||
<div | ||
style={{ maxHeight: '600px', position: 'relative', overflow: 'hidden' }} | ||
> | ||
{!full && !isPlaying && ( | ||
<div | ||
style={{ | ||
position: 'absolute', | ||
top: 0, | ||
bottom: 0, | ||
left: 0, | ||
right: 0, | ||
display: 'flex', | ||
flexDirection: 'column', | ||
justifyContent: 'center', | ||
padding: '20px', | ||
background: `rgba(0, 0, 0, ${ | ||
typeof src === 'string' | ||
? isImage(src) | ||
? '0.20' | ||
: '0.60' | ||
: isImage(src.url) | ||
? '0.20' | ||
: '0.60' | ||
})`, | ||
alignItems: isCentered ? 'center' : 'left', | ||
}} | ||
> | ||
<div | ||
style={{ | ||
marginLeft: title || subtitle ? '6em' : '0', | ||
zIndex: 50, | ||
}} | ||
> | ||
<VideoText title={title} subtitle={subtitle} theme={theme} /> | ||
{typeof src === 'string' | ||
? !isImage(src) && | ||
isVideo(src) && ( | ||
<div | ||
onClick={play} | ||
style={{ | ||
display: 'flex', | ||
flexDirection: 'row', | ||
width: 'fit-content', | ||
gap: '0.5em', | ||
alignItems: 'center', | ||
justifyContent: isCentered ? 'center' : 'left', | ||
color: textColor, | ||
}} | ||
> | ||
<p | ||
style={{ | ||
display: 'flex', | ||
fontWeight: 700, | ||
fontSize: '24px', | ||
textDecoration: 'none', | ||
color: textColor, | ||
cursor: 'pointer', | ||
alignSelf: 'flex-start', | ||
}} | ||
> | ||
{playButtonLabel} | ||
</p> | ||
<PlayArrowIcon | ||
sx={{ | ||
height: '2em', | ||
cursor: 'pointer', | ||
color: textColor, | ||
}} | ||
/> | ||
</div> | ||
) | ||
: !isImage(src.url) && | ||
isVideo(src.url) && ( | ||
<div | ||
onClick={play} | ||
style={{ | ||
display: 'flex', | ||
flexDirection: 'row', | ||
width: 'fit-content', | ||
gap: '0.5em', | ||
alignItems: 'center', | ||
justifyContent: isCentered ? 'center' : 'left', | ||
color: textColor, | ||
}} | ||
> | ||
<p | ||
style={{ | ||
display: 'flex', | ||
fontWeight: 700, | ||
fontSize: '24px', | ||
textDecoration: 'none', | ||
color: textColor, | ||
cursor: 'pointer', | ||
alignSelf: 'flex-start', | ||
}} | ||
> | ||
{playButtonLabel} | ||
</p> | ||
<PlayArrowIcon | ||
sx={{ | ||
height: '2em', | ||
cursor: 'pointer', | ||
color: textColor, | ||
}} | ||
/> | ||
</div> | ||
)} | ||
</div> | ||
</div> | ||
)} | ||
{typeof src === 'string' | ||
? isImage(src) | ||
? renderImage({ | ||
src, | ||
alt, | ||
}) | ||
: renderVideo({ | ||
videoRef, | ||
error, | ||
setError, | ||
src, | ||
loop, | ||
autoplay, | ||
fallback, | ||
onVideoEnd: handleVideoEnd, | ||
}) | ||
: isImage(src.url) | ||
? renderImage({ | ||
src: src.url, | ||
alt, | ||
}) | ||
: renderVideo({ | ||
videoRef, | ||
error, | ||
setError, | ||
src: src.url, | ||
loop, | ||
autoplay, | ||
fallback, | ||
onVideoEnd: handleVideoEnd, | ||
})} | ||
</div> | ||
{caption && ( | ||
<VideoCaption | ||
caption={caption} | ||
theme={theme} | ||
toBeCentered={isCentered} | ||
/> | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
export default VideoImage; |
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
Oops, something went wrong.