First Commit
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
// @mui
|
||||
import Button from '@mui/material/Button';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Tab from '@mui/material/Tab';
|
||||
import TabContext from '@mui/lab/TabContext';
|
||||
import TabList from '@mui/lab/TabList';
|
||||
import TabPanel from '@mui/lab/TabPanel';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
// @project
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import { GraphicsCard } from '@/components/cards';
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
import Typeset from '@/components/Typeset';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
// @assets
|
||||
import ButtonAnimationWrapper from '@/components/ButtonAnimationWrapper';
|
||||
import GraphicsImage from '@/components/GraphicsImage';
|
||||
|
||||
/*************************** FEATURE - 18 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Feature18](https://www.saasable.io/blocks/feature/feature18)
|
||||
*
|
||||
* API
|
||||
* - [Feature18 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/feature/feature18#props-details)
|
||||
*/
|
||||
|
||||
export default function Feature18({ heading, caption, topics }) {
|
||||
const boxPadding = { xs: 3, md: 5 };
|
||||
const imagePadding = { xs: 3, sm: 4, md: 5 };
|
||||
|
||||
const [value, setValue] = useState('1');
|
||||
|
||||
// Handle tab change
|
||||
const handleChange = (event, newValue) => {
|
||||
setValue(newValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<Stack sx={{ gap: { xs: 3, sm: 4 } }}>
|
||||
<Typeset
|
||||
{...{
|
||||
heading,
|
||||
caption,
|
||||
stackProps: { sx: { alignItems: 'center', textAlign: 'center', maxWidth: { sm: 470, md: 615 }, mx: 'auto' } }
|
||||
}}
|
||||
/>
|
||||
<Stack sx={{ gap: 1.5, alignItems: 'center' }}>
|
||||
<TabContext value={value}>
|
||||
<GraphicsCard sx={{ width: { xs: 1, sm: 'unset' } }}>
|
||||
<Box sx={{ p: 0.25 }}>
|
||||
<TabList
|
||||
onChange={handleChange}
|
||||
sx={{ minHeight: 'unset', p: 0.25 }}
|
||||
slotProps={{ indicator: { sx: { display: 'none' } } }}
|
||||
variant="scrollable"
|
||||
>
|
||||
{topics.map((item, index) => (
|
||||
<Tab
|
||||
label={item.title}
|
||||
disableFocusRipple
|
||||
{...(item.icon && {
|
||||
icon: (
|
||||
<SvgIcon
|
||||
{...(typeof item.icon === 'string' ? { name: item.icon } : { ...item.icon })}
|
||||
size={16}
|
||||
stroke={2}
|
||||
color="text.secondary"
|
||||
/>
|
||||
)
|
||||
})}
|
||||
value={String(index + 1)}
|
||||
key={index}
|
||||
iconPosition="start"
|
||||
tabIndex={0}
|
||||
sx={{
|
||||
minHeight: 44,
|
||||
minWidth: { xs: 112, md: 160, sm: 156 },
|
||||
borderRadius: 10,
|
||||
borderWidth: 1,
|
||||
borderStyle: 'solid',
|
||||
borderColor: 'transparent',
|
||||
'& svg ': { mr: 1 },
|
||||
'&.Mui-selected': {
|
||||
bgcolor: 'grey.200',
|
||||
borderColor: 'grey.400',
|
||||
minWidth: { xs: 112, md: 160, sm: 156 },
|
||||
color: 'text.primary',
|
||||
'& svg': { stroke: 'text.primary' }
|
||||
},
|
||||
'&.Mui-focusVisible': { bgcolor: 'grey.300' },
|
||||
'&:hover': { bgcolor: 'grey.200' }
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</TabList>
|
||||
</Box>
|
||||
</GraphicsCard>
|
||||
{topics.map((item, index) => (
|
||||
<TabPanel value={String(index + 1)} key={index} sx={{ p: 0, width: 1 }}>
|
||||
<Grid container spacing={1.5}>
|
||||
<Grid size={{ xs: 12, sm: 5 }}>
|
||||
<GraphicsCard>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 100, y: 100 }}
|
||||
whileInView={{ opacity: 1, x: 0, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
pl: item.isCoverImage ? 0 : imagePadding,
|
||||
pt: item.isCoverImage ? 0 : imagePadding,
|
||||
height: { xs: 260, sm: 396, md: 434 }
|
||||
}}
|
||||
>
|
||||
<GraphicsImage
|
||||
sx={{
|
||||
height: 1,
|
||||
backgroundPositionX: 'left',
|
||||
backgroundPositionY: 'top',
|
||||
...(item.isImageBorder && { borderTop: '5px solid', borderLeft: '5px solid', borderColor: 'grey.200' }),
|
||||
...(item.isCoverImage && { backgroundSize: 'cover', border: 'none' }),
|
||||
borderTopLeftRadius: { xs: 12 },
|
||||
borderBottomRightRadius: { xs: 20, sm: 32, md: 40 }
|
||||
}}
|
||||
image={item.image}
|
||||
/>
|
||||
</Box>
|
||||
</motion.div>
|
||||
</GraphicsCard>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 7 }} sx={{ display: 'flex' }}>
|
||||
<GraphicsCard>
|
||||
<Stack
|
||||
sx={{
|
||||
justifyContent: 'space-between',
|
||||
gap: 5,
|
||||
height: item.actionBtn || item.actionBtn2 ? { sm: 'calc(100% - 98px)', md: 'calc(100% - 114px)' } : 1,
|
||||
pt: boxPadding,
|
||||
px: boxPadding
|
||||
}}
|
||||
>
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: -30 }}
|
||||
transition={{ duration: 0.2, ease: 'linear', delay: index * 0.1 }}
|
||||
>
|
||||
<Stack direction="row" sx={{ gap: 1 }}>
|
||||
{item.icon && (
|
||||
<SvgIcon
|
||||
{...(typeof item.icon === 'string' ? { name: item.icon } : { ...item.icon })}
|
||||
size={16}
|
||||
stroke={2}
|
||||
color="text.primary"
|
||||
/>
|
||||
)}
|
||||
<Typography variant="subtitle2" sx={{ color: 'text.secondary' }}>
|
||||
{item.title}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</motion.div>
|
||||
<Stack sx={{ gap: { xs: 2, md: 3 }, pb: boxPadding }}>
|
||||
<Stack sx={{ gap: 0.5 }}>
|
||||
<Typography variant="h4">{item.title2}</Typography>
|
||||
{item.description && <Typography sx={{ color: 'text.secondary' }}>{item.description}</Typography>}
|
||||
</Stack>
|
||||
{item.list && (
|
||||
<Grid container spacing={{ xs: 0.75, md: 1 }}>
|
||||
{item.list.map((list, index) => (
|
||||
<Grid key={index} size={{ xs: 12, md: 6 }}>
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: -30 }}
|
||||
transition={{ duration: 0.2, ease: 'linear', delay: index * 0.1 }}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
sx={{
|
||||
gap: 0.5,
|
||||
alignItems: 'center',
|
||||
'& svg.tabler-rosette-discount-check': { width: { xs: 16, md: 24 }, height: { xs: 16, md: 24 } }
|
||||
}}
|
||||
>
|
||||
<SvgIcon name="tabler-rosette-discount-check" stroke={1} color="text.secondary" />
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
|
||||
{list.primary}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</motion.div>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
{(item.actionBtn || item.actionBtn2) && (
|
||||
<GraphicsCard sx={{ bgcolor: 'grey.200' }}>
|
||||
<Stack direction="row" sx={{ alignItems: 'flex-start', gap: 1.5, p: { xs: 2, sm: 3, md: 4 } }}>
|
||||
{item.actionBtn2 && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
whileHover={{ scale: 1.06 }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
startIcon={<SvgIcon name="tabler-help" size={16} stroke={3} />}
|
||||
{...item.actionBtn2}
|
||||
/>
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
)}
|
||||
{item.actionBtn && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
whileHover={{ scale: 1.06 }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<SvgIcon name="tabler-link" size={16} stroke={3} color="background.default" />}
|
||||
{...item.actionBtn}
|
||||
/>
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
)}
|
||||
</Stack>
|
||||
</GraphicsCard>
|
||||
)}
|
||||
</GraphicsCard>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</TabPanel>
|
||||
))}
|
||||
</TabContext>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
Feature18.propTypes = { heading: PropTypes.string, caption: PropTypes.string, topics: PropTypes.array };
|
||||
@@ -0,0 +1,248 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @mui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import Avatar from '@mui/material/Avatar';
|
||||
import Button from '@mui/material/Button';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
// @project
|
||||
import ButtonAnimationWrapper from '@/components/ButtonAnimationWrapper';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import { GraphicsCard } from '@/components/cards';
|
||||
import GraphicsImage from '@/components/GraphicsImage';
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
import Typeset from '@/components/Typeset';
|
||||
import { withAlpha } from '@/utils/colorUtils';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
// @assets
|
||||
import Star from '@/images/graphics/Star';
|
||||
|
||||
/*************************** FEATURE - 20 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Feature20](https://www.saasable.io/blocks/feature/feature20)
|
||||
*
|
||||
* API
|
||||
* - [Feature20 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/feature/feature20#props-details)
|
||||
*/
|
||||
|
||||
export default function Feature20({ heading, caption, image, features, actionBtn, secondaryBtn }) {
|
||||
const theme = useTheme();
|
||||
const downSM = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
const downMD = useMediaQuery(theme.breakpoints.down('md'));
|
||||
|
||||
const partitionInExtraSmall = 1;
|
||||
const partitionInSmall = 2;
|
||||
const partitionInLarge = 3;
|
||||
|
||||
const columns = downSM ? partitionInExtraSmall : downMD ? partitionInSmall : partitionInLarge;
|
||||
|
||||
const calculateElementsInLastRow = (dataArray, columns) => {
|
||||
const totalItems = dataArray.length;
|
||||
const elementsInLastRow = totalItems % columns || columns;
|
||||
return elementsInLastRow;
|
||||
};
|
||||
|
||||
const calculateIndexOfFirstElementInLastRow = (dataArray, elementsInLastRow) => {
|
||||
const totalItems = dataArray.length;
|
||||
const indexOfFirstElementInLastRow = totalItems - elementsInLastRow;
|
||||
return indexOfFirstElementInLastRow;
|
||||
};
|
||||
|
||||
const elementsInLastRow = calculateElementsInLastRow(features, columns);
|
||||
const indexOfFirstElementInLastRow = calculateIndexOfFirstElementInLastRow(features, elementsInLastRow);
|
||||
|
||||
const calculateIndexOfLastElementOfEachRow = (dataArray, columns) => {
|
||||
const indices = [];
|
||||
const totalItems = dataArray.length;
|
||||
const rows = Math.ceil(totalItems / columns);
|
||||
|
||||
for (let i = 1; i <= rows; i++) {
|
||||
const lastIndexInRow = i * columns - 1;
|
||||
indices.push(lastIndexInRow < totalItems ? lastIndexInRow : totalItems - 1);
|
||||
}
|
||||
|
||||
return indices;
|
||||
};
|
||||
|
||||
const indicesOfLastElements = calculateIndexOfLastElementOfEachRow(features, columns);
|
||||
const gc = theme.vars.palette.background.default;
|
||||
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<Stack sx={{ gap: { xs: 3, sm: 4, md: 5 } }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: 0.3
|
||||
}}
|
||||
>
|
||||
<Typeset {...{ heading, stackProps: { sx: { maxWidth: { md: 500 }, ...(!image && { maxWidth: 1, textAlign: 'center' }) } } }} />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 25 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: 0.4
|
||||
}}
|
||||
>
|
||||
<GraphicsCard sx={{ position: 'relative', overflow: 'visible' }}>
|
||||
{image && (
|
||||
<GraphicsCard
|
||||
sx={{
|
||||
height: { md: 267 },
|
||||
width: { md: 456 },
|
||||
bgcolor: 'transparent',
|
||||
position: 'absolute',
|
||||
top: -190,
|
||||
right: 45,
|
||||
zIndex: -1,
|
||||
display: { xs: 'none', md: 'block' }
|
||||
}}
|
||||
>
|
||||
<GraphicsImage sx={{ height: 1, backgroundPositionX: 'right', backgroundPositionY: 'top' }} image={image}>
|
||||
<Box
|
||||
sx={{ width: 1, height: 1, background: `linear-gradient(180deg, ${withAlpha(gc, 0)} 0%, ${withAlpha(gc, 0.6)} 100%)` }}
|
||||
/>
|
||||
</GraphicsImage>
|
||||
</GraphicsCard>
|
||||
)}
|
||||
<Box sx={{ p: 3 }}>
|
||||
<Grid container>
|
||||
{features.map((item, index) => (
|
||||
<Grid
|
||||
key={index}
|
||||
size={{ xs: 12 / partitionInExtraSmall, sm: 12 / partitionInSmall, md: 12 / partitionInLarge }}
|
||||
sx={{
|
||||
position: 'relative',
|
||||
...(index < indexOfFirstElementInLastRow && { borderBottom: `1px solid ${theme.vars.palette.grey[300]}` }),
|
||||
...(!indicesOfLastElements.includes(index) && { borderRight: `1px solid ${theme.vars.palette.grey[300]}` })
|
||||
}}
|
||||
>
|
||||
<Stack sx={{ gap: { xs: 3, sm: 4 }, height: 1, py: { xs: 1.5, sm: 3, md: 4 }, px: { xs: 0, sm: 3, md: 4 } }}>
|
||||
<Avatar sx={{ width: 60, height: 60, bgcolor: 'grey.300' }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.6 }}
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 2, delay: index * 0.1 }}
|
||||
>
|
||||
<SvgIcon {...(typeof item.icon === 'string' ? { name: item.icon } : { ...item.icon })} />
|
||||
</motion.div>
|
||||
</Avatar>
|
||||
<Stack sx={{ gap: { xs: 0.5, md: 1 } }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 25 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: index * 0.2 }}
|
||||
>
|
||||
{item.title && <Typography variant="h4">{item.title}</Typography>}
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 25 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: index * 0.3 }}
|
||||
>
|
||||
{item.content && <Typography sx={{ color: 'text.secondary' }}>{item.content}</Typography>}
|
||||
</motion.div>
|
||||
</Stack>
|
||||
</Stack>
|
||||
{index < indexOfFirstElementInLastRow && !indicesOfLastElements.includes(index) && (
|
||||
<Stack sx={{ position: 'absolute', bottom: -9, right: -9 }}>
|
||||
<Star />
|
||||
</Stack>
|
||||
)}
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
</GraphicsCard>
|
||||
</motion.div>
|
||||
<Stack sx={{ alignItems: 'center', gap: 3 }}>
|
||||
<Typography variant="h6" sx={{ color: 'text.secondary', maxWidth: { xs: '75%', sm: '45%' }, textAlign: 'center' }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 15 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: 0.4
|
||||
}}
|
||||
>
|
||||
{caption}
|
||||
</motion.div>
|
||||
</Typography>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: 0.5
|
||||
}}
|
||||
>
|
||||
<Stack direction="row" sx={{ alignItems: 'center', justifyContent: 'center', gap: 1.5 }}>
|
||||
{secondaryBtn && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
whileHover={{ scale: 1.06 }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button variant="outlined" {...secondaryBtn} />
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
)}
|
||||
{actionBtn && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
whileHover={{ scale: 1.06 }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<SvgIcon name="tabler-sparkles" size={16} stroke={3} color="background.default" />}
|
||||
{...actionBtn}
|
||||
/>
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
)}
|
||||
</Stack>
|
||||
</motion.div>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
Feature20.propTypes = {
|
||||
heading: PropTypes.string,
|
||||
caption: PropTypes.string,
|
||||
image: PropTypes.any,
|
||||
features: PropTypes.array,
|
||||
actionBtn: PropTypes.any,
|
||||
secondaryBtn: PropTypes.any
|
||||
};
|
||||
@@ -0,0 +1,153 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @mui
|
||||
import Button from '@mui/material/Button';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
// @project
|
||||
import ButtonAnimationWrapper from '@/components/ButtonAnimationWrapper';
|
||||
import { GraphicsCard, IconCard } from '@/components/cards';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import GraphicsImage from '@/components/GraphicsImage';
|
||||
import Typeset from '@/components/Typeset';
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
/*************************** FEATURE - 21 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Feature21](https://www.saasable.io/blocks/feature/feature21)
|
||||
*
|
||||
* API
|
||||
* - [Feature21 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/feature/feature21#props-details)
|
||||
*/
|
||||
|
||||
export default function Feature21({ heading, caption, image, features, primaryBtn, secondaryBtn }) {
|
||||
const imagePadding = { xs: 3, sm: 4, md: 5 };
|
||||
const iconProps = { color: 'text.primary', stroke: 1 };
|
||||
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<Stack sx={{ gap: { xs: 3, sm: 4 } }}>
|
||||
{(heading || caption) && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 5 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.3, delay: 0.3 }}
|
||||
>
|
||||
<Typeset {...{ heading, caption, stackProps: { sx: { textAlign: 'center' } } }} />
|
||||
</motion.div>
|
||||
)}
|
||||
<Stack sx={{ gap: 1.5 }}>
|
||||
{image && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.3,
|
||||
delay: 0.3
|
||||
}}
|
||||
>
|
||||
<GraphicsCard>
|
||||
<Box sx={{ pl: imagePadding, pt: imagePadding, height: { xs: 204, sm: 300, md: 360 } }}>
|
||||
<GraphicsImage
|
||||
image={image}
|
||||
sx={{
|
||||
height: 1,
|
||||
backgroundPositionX: 'left',
|
||||
backgroundPositionY: 'top',
|
||||
borderTopLeftRadius: { xs: 12 },
|
||||
borderBottomRightRadius: { xs: 20, sm: 32, md: 40 },
|
||||
border: '5px solid',
|
||||
borderColor: 'grey.200',
|
||||
borderBottom: 'none',
|
||||
borderRight: 'none'
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</GraphicsCard>
|
||||
</motion.div>
|
||||
)}
|
||||
<Grid container spacing={1.5}>
|
||||
{features.map((item, index) => (
|
||||
<Grid key={index} size={{ xs: 12, sm: 6, md: 3 }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.3, delay: index * 0.2 }}
|
||||
style={{ height: '100%' }}
|
||||
>
|
||||
<IconCard
|
||||
icon={{ ...(typeof item.icon === 'string' ? { name: item.icon, ...iconProps } : { ...iconProps, ...item.icon }) }}
|
||||
title={item.title}
|
||||
stackProps={{ sx: { gap: 2, height: 1 } }}
|
||||
cardPadding={{ xs: 2, sm: 3 }}
|
||||
/>
|
||||
</motion.div>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Stack>
|
||||
{(primaryBtn || secondaryBtn) && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.3, delay: 0.4 }}
|
||||
>
|
||||
<Stack direction="row" sx={{ alignItems: 'center', justifyContent: 'center', gap: 1.5 }}>
|
||||
{secondaryBtn && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
whileHover={{ scale: 1.06 }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button variant="outlined" startIcon={<SvgIcon name="tabler-eye" size={16} stroke={3} />} {...secondaryBtn} />
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
)}
|
||||
{primaryBtn && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
whileHover={{ scale: 1.06 }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<SvgIcon name="tabler-download" size={16} stroke={3} color="background.default" />}
|
||||
{...primaryBtn}
|
||||
/>
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
)}
|
||||
</Stack>
|
||||
</motion.div>
|
||||
)}
|
||||
</Stack>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
Feature21.propTypes = {
|
||||
heading: PropTypes.string,
|
||||
caption: PropTypes.string,
|
||||
image: PropTypes.any,
|
||||
features: PropTypes.array,
|
||||
primaryBtn: PropTypes.any,
|
||||
secondaryBtn: PropTypes.any
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export { default as Feature18 } from './Feature18';
|
||||
export { default as Feature20 } from './Feature20';
|
||||
export { default as Feature21 } from './Feature21';
|
||||
Reference in New Issue
Block a user