317 lines
11 KiB
TypeScript
317 lines
11 KiB
TypeScript
import {
|
|
Box,
|
|
Card,
|
|
CardContent,
|
|
CardMedia,
|
|
Typography,
|
|
Container,
|
|
useMediaQuery,
|
|
useTheme,
|
|
Grid,
|
|
} from "@mui/material";
|
|
import { motion, AnimatePresence, PanInfo } from "framer-motion";
|
|
import { useState } from "react";
|
|
import serviceImage from "../services/Image Wrapper.png";
|
|
|
|
const services = [
|
|
{
|
|
title: "Design",
|
|
description:
|
|
"Lorem ipsum dolor sit amet consectetur. Non aliquam justo hendrerit vitae lacus. Cursus purus dictumst scelerisque vitae ultricies turpis aliquam turpis.",
|
|
image: `${serviceImage}`,
|
|
},
|
|
{
|
|
title: "Development",
|
|
description:
|
|
"Lorem ipsum dolor sit amet consectetur. Non aliquam justo hendrerit vitae lacus. Cursus purus dictumst scelerisque vitae ultricies turpis aliquam turpis.",
|
|
image: `${serviceImage}`,
|
|
},
|
|
{
|
|
title: "Marketing",
|
|
description:
|
|
"Lorem ipsum dolor sit amet consectetur. Non aliquam justo hendrerit vitae lacus. Cursus purus dictumst scelerisque vitae ultricies turpis aliquam turpis.",
|
|
image: `${serviceImage}`,
|
|
},
|
|
];
|
|
|
|
const Services = () => {
|
|
const theme = useTheme();
|
|
const isMobile = useMediaQuery(theme.breakpoints.down("md"));
|
|
const [currentIndex, setCurrentIndex] = useState(0);
|
|
const [hoveredCard, setHoveredCard] = useState<number | null>(null);
|
|
|
|
const handleDragEnd = (
|
|
event: MouseEvent | TouchEvent | PointerEvent,
|
|
info: PanInfo
|
|
) => {
|
|
if (info.offset.x > 50) {
|
|
setCurrentIndex((prev) => (prev === 0 ? services.length - 1 : prev - 1));
|
|
} else if (info.offset.x < -50) {
|
|
setCurrentIndex((prev) => (prev === services.length - 1 ? 0 : prev + 1));
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Box sx={{ backgroundColor: "#1d2733", color: "#fff", py: 8 }}>
|
|
<Container maxWidth="lg">
|
|
<motion.div
|
|
initial={{ opacity: 0, y: -20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.6 }}
|
|
viewport={{ once: true, margin: "-100px" }}
|
|
>
|
|
<Typography
|
|
variant="h4"
|
|
align="center"
|
|
fontWeight="bold"
|
|
gutterBottom
|
|
sx={{ mb: 2 }}
|
|
>
|
|
Our Services
|
|
</Typography>
|
|
<Typography
|
|
variant="subtitle1"
|
|
align="center"
|
|
mb={6}
|
|
sx={{ maxWidth: 600, mx: "auto" }}
|
|
>
|
|
Professional solutions tailored to your needs
|
|
</Typography>
|
|
</motion.div>
|
|
|
|
{isMobile ? (
|
|
<Box sx={{ position: "relative", height: 380 }}>
|
|
<AnimatePresence initial={false}>
|
|
<motion.div
|
|
key={currentIndex}
|
|
drag="x"
|
|
onDragEnd={handleDragEnd}
|
|
dragConstraints={{ left: 0, right: 0 }}
|
|
initial={{ x: 300, opacity: 0 }}
|
|
animate={{ x: 0, opacity: 1 }}
|
|
exit={{ x: -300, opacity: 0 }}
|
|
transition={{ type: "spring", stiffness: 300, damping: 30 }}
|
|
style={{
|
|
position: "absolute",
|
|
width: "100%",
|
|
padding: "0 16px",
|
|
}}
|
|
>
|
|
<motion.div
|
|
whileHover={{ scale: 1.02 }}
|
|
whileTap={{ scale: 0.98 }}
|
|
>
|
|
<Card
|
|
sx={{
|
|
borderRadius: 2,
|
|
overflow: "hidden",
|
|
height: "100%",
|
|
bgcolor: "#fff",
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
maxWidth: 300,
|
|
mx: "auto",
|
|
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.1)",
|
|
}}
|
|
>
|
|
<Box
|
|
sx={{
|
|
width: "100%",
|
|
height: 150,
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
bgcolor: "#f8f9fa",
|
|
overflow: "hidden",
|
|
}}
|
|
>
|
|
<motion.div
|
|
animate={{
|
|
scale: hoveredCard === currentIndex ? 1.05 : 1,
|
|
y: hoveredCard === currentIndex ? -5 : 0,
|
|
}}
|
|
transition={{ duration: 0.3 }}
|
|
style={{ width: "100%" }}
|
|
>
|
|
<CardMedia
|
|
component="img"
|
|
image={services[currentIndex].image}
|
|
alt={services[currentIndex].title}
|
|
sx={{
|
|
width: "100%",
|
|
height: "auto",
|
|
objectFit: "cover",
|
|
}}
|
|
/>
|
|
</motion.div>
|
|
</Box>
|
|
<CardContent sx={{ flexGrow: 1 }}>
|
|
<Typography
|
|
variant="h6"
|
|
fontWeight="bold"
|
|
gutterBottom
|
|
color="#000"
|
|
>
|
|
{services[currentIndex].title}
|
|
</Typography>
|
|
<Typography variant="body2" color="#000" sx={{ mb: 2 }}>
|
|
{services[currentIndex].description}
|
|
</Typography>
|
|
<motion.div
|
|
whileHover={{ x: 5 }}
|
|
transition={{ type: "spring", stiffness: 300 }}
|
|
>
|
|
<Typography
|
|
variant="caption"
|
|
color="primary"
|
|
fontWeight="bold"
|
|
sx={{ cursor: "pointer" }}
|
|
>
|
|
Learn more →
|
|
</Typography>
|
|
</motion.div>
|
|
</CardContent>
|
|
</Card>
|
|
</motion.div>
|
|
</motion.div>
|
|
</AnimatePresence>
|
|
|
|
<Box
|
|
sx={{
|
|
display: "flex",
|
|
justifyContent: "center",
|
|
mt: 3,
|
|
gap: 1.5,
|
|
}}
|
|
>
|
|
{services.map((_, index) => (
|
|
<motion.div
|
|
key={index}
|
|
onClick={() => setCurrentIndex(index)}
|
|
whileHover={{ scale: 1.2 }}
|
|
whileTap={{ scale: 0.9 }}
|
|
>
|
|
<Box
|
|
sx={{
|
|
width: 10,
|
|
height: 10,
|
|
borderRadius: "50%",
|
|
bgcolor: index === currentIndex ? "#00E0FF" : "#ffffff80",
|
|
cursor: "pointer",
|
|
}}
|
|
/>
|
|
</motion.div>
|
|
))}
|
|
</Box>
|
|
</Box>
|
|
) : (
|
|
<Grid container spacing={3} justifyContent="center">
|
|
{services.map((service, index) => (
|
|
<Grid
|
|
item
|
|
xs={12}
|
|
sm={6}
|
|
md={4}
|
|
key={index}
|
|
sx={{ display: "flex", justifyContent: "center" }}
|
|
>
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 50 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.5, delay: index * 0.1 }}
|
|
viewport={{ once: true, margin: "-100px" }}
|
|
onHoverStart={() => setHoveredCard(index)}
|
|
onHoverEnd={() => setHoveredCard(null)}
|
|
style={{ width: "100%", maxWidth: 280 }}
|
|
>
|
|
<motion.div
|
|
whileHover={{
|
|
y: -8,
|
|
boxShadow: "0 15px 30px -5px rgba(0, 0, 0, 0.2)",
|
|
}}
|
|
transition={{ type: "spring", stiffness: 400, damping: 10 }}
|
|
style={{ height: "100%" }}
|
|
>
|
|
<Card
|
|
sx={{
|
|
borderRadius: 2,
|
|
overflow: "hidden",
|
|
height: "100%",
|
|
bgcolor: "#fff",
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
boxShadow: "0 5px 15px rgba(0, 0, 0, 0.1)",
|
|
width: "100%",
|
|
}}
|
|
>
|
|
<Box
|
|
sx={{
|
|
width: "100%",
|
|
height: 160,
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
bgcolor: "#f8f9fa",
|
|
overflow: "hidden",
|
|
}}
|
|
>
|
|
<motion.div
|
|
animate={{
|
|
scale: hoveredCard === index ? 1.1 : 1,
|
|
y: hoveredCard === index ? -5 : 0,
|
|
}}
|
|
transition={{ duration: 0.3 }}
|
|
style={{ width: "100%" }}
|
|
>
|
|
<CardMedia
|
|
component="img"
|
|
image={service.image}
|
|
alt={service.title}
|
|
sx={{
|
|
width: "100%",
|
|
height: "auto",
|
|
objectFit: "cover",
|
|
}}
|
|
/>
|
|
</motion.div>
|
|
</Box>
|
|
<CardContent sx={{ flexGrow: 1 }}>
|
|
<Typography
|
|
variant="h6"
|
|
fontWeight="bold"
|
|
gutterBottom
|
|
color="#000"
|
|
>
|
|
{service.title}
|
|
</Typography>
|
|
<Typography variant="body2" color="#000" sx={{ mb: 2 }}>
|
|
{service.description}
|
|
</Typography>
|
|
<motion.div
|
|
whileHover={{ x: 5 }}
|
|
transition={{ type: "spring", stiffness: 300 }}
|
|
>
|
|
<Typography
|
|
variant="caption"
|
|
color="primary"
|
|
fontWeight="bold"
|
|
sx={{ cursor: "pointer" }}
|
|
>
|
|
Learn more →
|
|
</Typography>
|
|
</motion.div>
|
|
</CardContent>
|
|
</Card>
|
|
</motion.div>
|
|
</motion.div>
|
|
</Grid>
|
|
))}
|
|
</Grid>
|
|
)}
|
|
</Container>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export default Services;
|