Files
jobtrackingapp/vendor/saasable-ui-main/uikit/react/src/blocks/hero/Hero17.jsx
T
2026-03-21 11:55:27 +01:00

251 lines
8.6 KiB
React

'use client';
import PropTypes from 'prop-types';
import { useEffect, useRef, useState } from 'react';
// @mui
import { useTheme } from '@mui/material/styles';
import Button from '@mui/material/Button';
import Chip from '@mui/material/Chip';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
// @third-party
import { motion, useScroll, useTransform } from 'motion/react';
// @project
import ButtonAnimationWrapper from '@/components/ButtonAnimationWrapper';
import { GraphicsCard } from '@/components/cards';
import ContainerWrapper from '@/components/ContainerWrapper';
import GraphicsImage from '@/components/GraphicsImage';
import SvgIcon from '@/components/SvgIcon';
import { SECTION_COMMON_PY } from '@/utils/constant';
import { getBackgroundDots } from '@/utils/getBackgroundDots';
import { withAlpha } from '@/utils/colorUtils';
// @assets
import Wave from '@/images/graphics/Wave';
// threshold - adjust threshold as needed
const options = { root: null, rootMargin: '0px', threshold: 0.6 };
/*************************** HERO - 17 ***************************/
/**
*
* Demos:
* - [Hero17](https://www.saasable.io/blocks/hero/hero17)
*
* API:
* - [Hero17 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/hero/hero17#props-details)
*/
export default function Hero17({ chip, headLine, captionLine, primaryBtn, videoSrc, videoThumbnail, listData }) {
const theme = useTheme();
const boxRadius = { xs: 24, sm: 32, md: 40 };
const containerRef = useRef(null);
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ['start start', 'end start']
});
const scale = useTransform(scrollYProgress, [0, 0.1, 0.2, 0.4, 0.6], [0.9, 0.92, 0.94, 0.96, 1]);
const videoRef = useRef(null);
const [isPlaying, setIsPlaying] = useState(false);
// Handle video play/pause based on intersection with the viewport
useEffect(() => {
const handleIntersection = (entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
if (videoRef.current && !isPlaying) {
videoRef.current
.play()
.then(() => {
setIsPlaying(true);
})
.catch((error) => {
console.error('Autoplay was prevented:', error);
});
}
} else {
if (videoRef.current && isPlaying) {
videoRef.current.pause();
setIsPlaying(false);
}
}
});
};
const observer = new IntersectionObserver(handleIntersection, options);
const videoElement = videoRef.current;
if (videoElement) {
observer.observe(videoElement);
}
return () => {
if (videoElement) {
observer.unobserve(videoElement);
}
};
}, [isPlaying]);
return (
<>
<Box
sx={{
height: { xs: 592, sm: 738, md: 878 },
position: 'absolute',
top: 0,
left: 0,
width: 1,
zIndex: -1,
borderBottomLeftRadius: boxRadius,
borderBottomRightRadius: boxRadius,
...getBackgroundDots(theme.vars.palette.grey[300], 2, 35),
bgcolor: 'grey.100'
}}
/>
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
<Box ref={containerRef}>
<Box sx={{ pb: { xs: 3, sm: 4, md: 5 } }}>
<Stack sx={{ alignItems: 'center', gap: 1.5 }}>
<motion.div
initial={{ opacity: 0, scale: 0.6 }}
whileInView={{ opacity: 1, scale: [0.6, 1.15, 0.95, 1] }}
animate={{
boxShadow: [
`0 0 0px ${withAlpha(theme.vars.palette.primary.dark, 0)}`,
`0 0 20px ${withAlpha(theme.vars.palette.primary.main, 0.8)}`,
`0 0 0px ${withAlpha(theme.vars.palette.primary.dark, 0)}`
],
borderRadius: '74px'
}}
viewport={{ once: true }}
transition={{ duration: 0.6, delay: 0.8, ease: 'linear' }}
>
<Chip
variant="outlined"
label={chip.label}
slotProps={{
label: {
sx: { py: 0.5, px: 1.5, ...(typeof chip.label === 'string' && { typography: 'caption', color: 'text.secondary' }) }
}
}}
sx={{ bgcolor: 'grey.100' }}
/>
</motion.div>
<motion.div
initial={{ opacity: 0, scale: 0.6 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.6, delay: 0.2, ease: 'linear' }}
>
<Typography variant="h1" align="center" sx={{ maxWidth: 800 }}>
{headLine}
</Typography>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 1, delay: 0.2, ease: [0.215, 0.61, 0.355, 1] }}
>
<Box sx={{ pt: 0.5, pb: 0.75 }}>
<Wave />
</Box>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 1, delay: 0.3, ease: [0.215, 0.61, 0.355, 1] }}
>
<Typography variant="h6" align="center" sx={{ color: 'text.secondary', maxWidth: 650 }}>
{captionLine}
</Typography>
</motion.div>
</Stack>
<Stack sx={{ alignItems: 'center', gap: 2, mt: { xs: 3, sm: 4, md: 5 } }}>
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, ease: 'easeInOut' }}
whileHover={{ scale: 1.06 }}
>
<ButtonAnimationWrapper>
<Button
color="primary"
variant="contained"
startIcon={<SvgIcon name="tabler-sparkles" size={16} stroke={3} color="background.default" />}
{...primaryBtn}
/>
</ButtonAnimationWrapper>
</motion.div>
<Stack direction="row" sx={{ gap: 1, flexWrap: 'wrap', justifyContent: 'center' }}>
{listData.map((item, index) => (
<motion.div
key={index}
initial={{ opacity: 0, scale: 0.6 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.9, delay: index * 0.08, ease: 'linear' }}
>
<Chip
label={item.title}
variant="outlined"
icon={<GraphicsImage image={item.image} sx={{ width: 16, height: 16 }} />}
slotProps={{ label: { sx: { py: 0.75, px: 1, typography: 'caption2' } } }}
sx={{ height: 32, px: 1, bgcolor: 'grey.100' }}
/>
</motion.div>
))}
</Stack>
</Stack>
</Box>
<motion.div
initial={{ opacity: 0, scale: 0.6 }}
whileInView={{ opacity: 1, scale: 0.9 }}
viewport={{ once: true }}
transition={{ duration: 0.9, delay: 0.3 }}
style={{ scale }}
>
<GraphicsCard sx={{ border: '5px solid', borderColor: 'grey.300' }}>
<video
playsInline
ref={videoRef}
width="100%"
height="100%"
style={{ display: 'flex', objectFit: 'cover' }}
preload="metadata"
autoPlay={false}
loop={true}
muted={true}
poster={videoThumbnail}
>
<source src={videoSrc} type="video/mp4" />
</video>
</GraphicsCard>
</motion.div>
</Box>
</ContainerWrapper>
</>
);
}
Hero17.propTypes = {
chip: PropTypes.object,
headLine: PropTypes.string,
captionLine: PropTypes.string,
primaryBtn: PropTypes.any,
videoSrc: PropTypes.string,
videoThumbnail: PropTypes.string,
listData: PropTypes.array
};