mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-11-12 03:13:41 +08:00
feat: add new theme berry (#860)
* feat: add theme berry * docs: add development notes * fix: fix blank page * chore: update implementation * fix: fix package.json * chore: update ui copy --------- Co-authored-by: JustSong <songquanpeng@foxmail.com>
This commit is contained in:
11
web/berry/src/ui-component/AdminContainer.js
Normal file
11
web/berry/src/ui-component/AdminContainer.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { Container } from '@mui/material';
|
||||
|
||||
const AdminContainer = styled(Container)(({ theme }) => ({
|
||||
[theme.breakpoints.down('md')]: {
|
||||
paddingLeft: '0px',
|
||||
paddingRight: '0px'
|
||||
}
|
||||
}));
|
||||
|
||||
export default AdminContainer;
|
||||
37
web/berry/src/ui-component/Footer.js
Normal file
37
web/berry/src/ui-component/Footer.js
Normal file
@@ -0,0 +1,37 @@
|
||||
// material-ui
|
||||
import { Link, Container, Box } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
// ==============================|| FOOTER - AUTHENTICATION 2 & 3 ||============================== //
|
||||
|
||||
const Footer = () => {
|
||||
const siteInfo = useSelector((state) => state.siteInfo);
|
||||
|
||||
return (
|
||||
<Container sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '64px' }}>
|
||||
<Box sx={{ textAlign: 'center' }}>
|
||||
{siteInfo.footer_html ? (
|
||||
<div className="custom-footer" dangerouslySetInnerHTML={{ __html: siteInfo.footer_html }}></div>
|
||||
) : (
|
||||
<>
|
||||
<Link href="https://github.com/songquanpeng/one-api" target="_blank">
|
||||
{siteInfo.system_name} {process.env.REACT_APP_VERSION}{' '}
|
||||
</Link>
|
||||
由{' '}
|
||||
<Link href="https://github.com/songquanpeng" target="_blank">
|
||||
JustSong
|
||||
</Link>{' '}
|
||||
构建,主题 berry 来自{' '}
|
||||
<Link href="https://github.com/MartialBE" target="_blank">
|
||||
MartialBE
|
||||
</Link>{' '},源代码遵循
|
||||
<Link href="https://opensource.org/licenses/mit-license.php"> MIT 协议</Link>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
158
web/berry/src/ui-component/Label.js
Normal file
158
web/berry/src/ui-component/Label.js
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Label.js
|
||||
*
|
||||
* This file uses code from the Minimal UI project, available at
|
||||
* https://github.com/minimal-ui-kit/material-kit-react/blob/main/src/components/label/label.jsx
|
||||
*
|
||||
* Minimal UI is licensed under the MIT License. A copy of the license is included below:
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021 Minimal UI (https://minimals.cc/)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import PropTypes from 'prop-types';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
import Box from '@mui/material/Box';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { alpha, styled } from '@mui/material/styles';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const Label = forwardRef(({ children, color = 'default', variant = 'soft', startIcon, endIcon, sx, ...other }, ref) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const iconStyles = {
|
||||
width: 16,
|
||||
height: 16,
|
||||
'& svg, img': { width: 1, height: 1, objectFit: 'cover' }
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledLabel
|
||||
ref={ref}
|
||||
component="span"
|
||||
ownerState={{ color, variant }}
|
||||
sx={{
|
||||
...(startIcon && { pl: 0.75 }),
|
||||
...(endIcon && { pr: 0.75 }),
|
||||
...sx
|
||||
}}
|
||||
theme={theme}
|
||||
{...other}
|
||||
>
|
||||
{startIcon && <Box sx={{ mr: 0.75, ...iconStyles }}> {startIcon} </Box>}
|
||||
|
||||
{children}
|
||||
|
||||
{endIcon && <Box sx={{ ml: 0.75, ...iconStyles }}> {endIcon} </Box>}
|
||||
</StyledLabel>
|
||||
);
|
||||
});
|
||||
|
||||
Label.propTypes = {
|
||||
children: PropTypes.node,
|
||||
endIcon: PropTypes.object,
|
||||
startIcon: PropTypes.object,
|
||||
sx: PropTypes.object,
|
||||
variant: PropTypes.oneOf(['filled', 'outlined', 'ghost', 'soft']),
|
||||
color: PropTypes.oneOf(['default', 'primary', 'secondary', 'info', 'success', 'warning', 'orange', 'error'])
|
||||
};
|
||||
|
||||
export default Label;
|
||||
|
||||
const StyledLabel = styled(Box)(({ theme, ownerState }) => {
|
||||
// const lightMode = theme.palette.mode === 'light';
|
||||
|
||||
const filledVariant = ownerState.variant === 'filled';
|
||||
|
||||
const outlinedVariant = ownerState.variant === 'outlined';
|
||||
|
||||
const softVariant = ownerState.variant === 'soft';
|
||||
|
||||
const ghostVariant = ownerState.variant === 'ghost';
|
||||
|
||||
const defaultStyle = {
|
||||
...(ownerState.color === 'default' && {
|
||||
// FILLED
|
||||
...(filledVariant && {
|
||||
color: theme.palette.grey[300],
|
||||
backgroundColor: theme.palette.text.primary
|
||||
}),
|
||||
// OUTLINED
|
||||
...(outlinedVariant && {
|
||||
color: theme.palette.grey[500],
|
||||
border: `2px solid ${theme.palette.grey[500]}`
|
||||
}),
|
||||
// SOFT
|
||||
...(softVariant && {
|
||||
color: theme.palette.text.secondary,
|
||||
backgroundColor: alpha(theme.palette.grey[500], 0.16)
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
const colorStyle = {
|
||||
...(ownerState.color !== 'default' && {
|
||||
// FILLED
|
||||
...(filledVariant && {
|
||||
color: theme.palette.background.paper,
|
||||
backgroundColor: theme.palette[ownerState.color]?.main
|
||||
}),
|
||||
// OUTLINED
|
||||
...(outlinedVariant && {
|
||||
backgroundColor: 'transparent',
|
||||
color: theme.palette[ownerState.color]?.main,
|
||||
border: `2px solid ${theme.palette[ownerState.color]?.main}`
|
||||
}),
|
||||
// SOFT
|
||||
...(softVariant && {
|
||||
color: theme.palette[ownerState.color]['dark'],
|
||||
backgroundColor: alpha(theme.palette[ownerState.color]?.main, 0.16)
|
||||
}),
|
||||
// GHOST
|
||||
...(ghostVariant && {
|
||||
color: theme.palette[ownerState.color]?.main
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
return {
|
||||
height: 24,
|
||||
minWidth: 24,
|
||||
lineHeight: 0,
|
||||
borderRadius: 6,
|
||||
cursor: 'default',
|
||||
alignItems: 'center',
|
||||
whiteSpace: 'nowrap',
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'center',
|
||||
// textTransform: 'capitalize',
|
||||
padding: theme.spacing(0, 0.75),
|
||||
fontSize: theme.typography.pxToRem(12),
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
transition: theme.transitions.create('all', {
|
||||
duration: theme.transitions.duration.shorter
|
||||
}),
|
||||
...defaultStyle,
|
||||
...colorStyle
|
||||
};
|
||||
});
|
||||
15
web/berry/src/ui-component/Loadable.js
Normal file
15
web/berry/src/ui-component/Loadable.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Suspense } from 'react';
|
||||
|
||||
// project imports
|
||||
import Loader from './Loader';
|
||||
|
||||
// ==============================|| LOADABLE - LAZY LOADING ||============================== //
|
||||
|
||||
const Loadable = (Component) => (props) =>
|
||||
(
|
||||
<Suspense fallback={<Loader />}>
|
||||
<Component {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
export default Loadable;
|
||||
21
web/berry/src/ui-component/Loader.js
Normal file
21
web/berry/src/ui-component/Loader.js
Normal file
@@ -0,0 +1,21 @@
|
||||
// material-ui
|
||||
import LinearProgress from '@mui/material/LinearProgress';
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
// styles
|
||||
const LoaderWrapper = styled('div')({
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
zIndex: 1301,
|
||||
width: '100%'
|
||||
});
|
||||
|
||||
// ==============================|| LOADER ||============================== //
|
||||
const Loader = () => (
|
||||
<LoaderWrapper>
|
||||
<LinearProgress color="primary" />
|
||||
</LoaderWrapper>
|
||||
);
|
||||
|
||||
export default Loader;
|
||||
21
web/berry/src/ui-component/Logo.js
Normal file
21
web/berry/src/ui-component/Logo.js
Normal file
@@ -0,0 +1,21 @@
|
||||
// material-ui
|
||||
import logo from 'assets/images/logo.svg';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
/**
|
||||
* if you want to use image instead of <svg> uncomment following.
|
||||
*
|
||||
* import logoDark from 'assets/images/logo-dark.svg';
|
||||
* import logo from 'assets/images/logo.svg';
|
||||
*
|
||||
*/
|
||||
|
||||
// ==============================|| LOGO SVG ||============================== //
|
||||
|
||||
const Logo = () => {
|
||||
const siteInfo = useSelector((state) => state.siteInfo);
|
||||
|
||||
return <img src={siteInfo.logo || logo} alt={siteInfo.system_name} width="80" />;
|
||||
};
|
||||
|
||||
export default Logo;
|
||||
31
web/berry/src/ui-component/SvgColor.js
Normal file
31
web/berry/src/ui-component/SvgColor.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const SvgColor = forwardRef(({ src, sx, ...other }, ref) => (
|
||||
<Box
|
||||
component="span"
|
||||
className="svg-color"
|
||||
ref={ref}
|
||||
sx={{
|
||||
width: 24,
|
||||
height: 24,
|
||||
display: 'inline-block',
|
||||
bgcolor: 'currentColor',
|
||||
mask: `url(${src}) no-repeat center / contain`,
|
||||
WebkitMask: `url(${src}) no-repeat center / contain`,
|
||||
...sx
|
||||
}}
|
||||
{...other}
|
||||
/>
|
||||
));
|
||||
|
||||
SvgColor.propTypes = {
|
||||
src: PropTypes.string,
|
||||
sx: PropTypes.object
|
||||
};
|
||||
|
||||
export default SvgColor;
|
||||
37
web/berry/src/ui-component/Switch.js
Normal file
37
web/berry/src/ui-component/Switch.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import { styled } from '@mui/material/styles';
|
||||
import Switch from '@mui/material/Switch';
|
||||
|
||||
const TableSwitch = styled(Switch)(({ theme }) => ({
|
||||
padding: 8,
|
||||
'& .MuiSwitch-track': {
|
||||
borderRadius: 22 / 2,
|
||||
'&:before, &:after': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
transform: 'translateY(-50%)',
|
||||
width: 16,
|
||||
height: 16
|
||||
},
|
||||
'&:before': {
|
||||
backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 24 24"><path fill="${encodeURIComponent(
|
||||
theme.palette.getContrastText(theme.palette.primary.main)
|
||||
)}" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"/></svg>')`,
|
||||
left: 12
|
||||
},
|
||||
'&:after': {
|
||||
backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 24 24"><path fill="${encodeURIComponent(
|
||||
theme.palette.getContrastText(theme.palette.primary.main)
|
||||
)}" d="M19,13H5V11H19V13Z" /></svg>')`,
|
||||
right: 12
|
||||
}
|
||||
},
|
||||
'& .MuiSwitch-thumb': {
|
||||
boxShadow: 'none',
|
||||
width: 16,
|
||||
height: 16,
|
||||
margin: 2
|
||||
}
|
||||
}));
|
||||
|
||||
export default TableSwitch;
|
||||
47
web/berry/src/ui-component/TableToolBar.js
Normal file
47
web/berry/src/ui-component/TableToolBar.js
Normal file
@@ -0,0 +1,47 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import OutlinedInput from '@mui/material/OutlinedInput';
|
||||
import InputAdornment from '@mui/material/InputAdornment';
|
||||
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { IconSearch } from '@tabler/icons-react';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export default function TableToolBar({ filterName, handleFilterName, placeholder }) {
|
||||
const theme = useTheme();
|
||||
const grey500 = theme.palette.grey[500];
|
||||
|
||||
return (
|
||||
<Toolbar
|
||||
sx={{
|
||||
height: 80,
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
p: (theme) => theme.spacing(0, 1, 0, 3)
|
||||
}}
|
||||
>
|
||||
<OutlinedInput
|
||||
id="keyword"
|
||||
sx={{
|
||||
minWidth: '100%'
|
||||
}}
|
||||
value={filterName}
|
||||
onChange={handleFilterName}
|
||||
placeholder={placeholder}
|
||||
startAdornment={
|
||||
<InputAdornment position="start">
|
||||
<IconSearch stroke={1.5} size="20px" color={grey500} />
|
||||
</InputAdornment>
|
||||
}
|
||||
/>
|
||||
</Toolbar>
|
||||
);
|
||||
}
|
||||
|
||||
TableToolBar.propTypes = {
|
||||
filterName: PropTypes.string,
|
||||
handleFilterName: PropTypes.func,
|
||||
placeholder: PropTypes.string
|
||||
};
|
||||
55
web/berry/src/ui-component/cards/CardSecondaryAction.js
Normal file
55
web/berry/src/ui-component/cards/CardSecondaryAction.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { ButtonBase, Link, Tooltip } from '@mui/material';
|
||||
|
||||
// project imports
|
||||
import Avatar from '../extended/Avatar';
|
||||
|
||||
// ==============================|| CARD SECONDARY ACTION ||============================== //
|
||||
|
||||
const CardSecondaryAction = ({ title, link, icon }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Tooltip title={title || 'Reference'} placement="left">
|
||||
<ButtonBase disableRipple>
|
||||
{!icon && (
|
||||
<Avatar component={Link} href={link} target="_blank" alt="MUI Logo" size="badge" color="primary" outline>
|
||||
<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clipPath="url(#clip0)">
|
||||
<path d="M100 260.9V131L212.5 195.95V239.25L137.5 195.95V282.55L100 260.9Z" fill={theme.palette.primary[800]} />
|
||||
<path
|
||||
d="M212.5 195.95L325 131V260.9L250 304.2L212.5 282.55L287.5 239.25V195.95L212.5 239.25V195.95Z"
|
||||
fill={theme.palette.primary.main}
|
||||
/>
|
||||
<path d="M212.5 282.55V325.85L287.5 369.15V325.85L212.5 282.55Z" fill={theme.palette.primary[800]} />
|
||||
<path
|
||||
d="M287.5 369.15L400 304.2V217.6L362.5 239.25V282.55L287.5 325.85V369.15ZM362.5 195.95V152.65L400 131V174.3L362.5 195.95Z"
|
||||
fill={theme.palette.primary.main}
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="300" height="238.3" fill="white" transform="translate(100 131)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</Avatar>
|
||||
)}
|
||||
{icon && (
|
||||
<Avatar component={Link} href={link} target="_blank" size="badge" color="primary" outline>
|
||||
{icon}
|
||||
</Avatar>
|
||||
)}
|
||||
</ButtonBase>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
CardSecondaryAction.propTypes = {
|
||||
icon: PropTypes.node,
|
||||
link: PropTypes.string,
|
||||
title: PropTypes.string
|
||||
};
|
||||
|
||||
export default CardSecondaryAction;
|
||||
80
web/berry/src/ui-component/cards/MainCard.js
Normal file
80
web/berry/src/ui-component/cards/MainCard.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { Card, CardContent, CardHeader, Divider, Typography } from '@mui/material';
|
||||
|
||||
// constant
|
||||
const headerSX = {
|
||||
'& .MuiCardHeader-action': { mr: 0 }
|
||||
};
|
||||
|
||||
// ==============================|| CUSTOM MAIN CARD ||============================== //
|
||||
|
||||
const MainCard = forwardRef(
|
||||
(
|
||||
{
|
||||
border = true,
|
||||
boxShadow,
|
||||
children,
|
||||
content = true,
|
||||
contentClass = '',
|
||||
contentSX = {},
|
||||
darkTitle,
|
||||
secondary,
|
||||
shadow,
|
||||
sx = {},
|
||||
title,
|
||||
...others
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Card
|
||||
ref={ref}
|
||||
{...others}
|
||||
sx={{
|
||||
border: border ? '1px solid' : 'none',
|
||||
borderColor: theme.palette.primary[200] + 25,
|
||||
':hover': {
|
||||
boxShadow: boxShadow ? shadow || '0 2px 14px 0 rgb(32 40 45 / 8%)' : 'inherit'
|
||||
},
|
||||
...sx
|
||||
}}
|
||||
>
|
||||
{/* card header and action */}
|
||||
{title && <CardHeader sx={headerSX} title={darkTitle ? <Typography variant="h3">{title}</Typography> : title} action={secondary} />}
|
||||
|
||||
{/* content & header divider */}
|
||||
{title && <Divider />}
|
||||
|
||||
{/* card content */}
|
||||
{content && (
|
||||
<CardContent sx={contentSX} className={contentClass}>
|
||||
{children}
|
||||
</CardContent>
|
||||
)}
|
||||
{!content && children}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
MainCard.propTypes = {
|
||||
border: PropTypes.bool,
|
||||
boxShadow: PropTypes.bool,
|
||||
children: PropTypes.node,
|
||||
content: PropTypes.bool,
|
||||
contentClass: PropTypes.string,
|
||||
contentSX: PropTypes.object,
|
||||
darkTitle: PropTypes.bool,
|
||||
secondary: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object]),
|
||||
shadow: PropTypes.string,
|
||||
sx: PropTypes.object,
|
||||
title: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object])
|
||||
};
|
||||
|
||||
export default MainCard;
|
||||
32
web/berry/src/ui-component/cards/Skeleton/EarningCard.js
Normal file
32
web/berry/src/ui-component/cards/Skeleton/EarningCard.js
Normal file
@@ -0,0 +1,32 @@
|
||||
// material-ui
|
||||
import { Card, CardContent, Grid } from '@mui/material';
|
||||
import Skeleton from '@mui/material/Skeleton';
|
||||
|
||||
// ==============================|| SKELETON - EARNING CARD ||============================== //
|
||||
|
||||
const EarningCard = () => (
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Grid container direction="column">
|
||||
<Grid item>
|
||||
<Grid container justifyContent="space-between">
|
||||
<Grid item>
|
||||
<Skeleton variant="rectangular" width={44} height={44} />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Skeleton variant="rectangular" width={34} height={34} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Skeleton variant="rectangular" sx={{ my: 2 }} height={40} />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Skeleton variant="rectangular" height={30} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
export default EarningCard;
|
||||
@@ -0,0 +1,8 @@
|
||||
// material-ui
|
||||
import Skeleton from '@mui/material/Skeleton';
|
||||
|
||||
// ==============================|| SKELETON IMAGE CARD ||============================== //
|
||||
|
||||
const ImagePlaceholder = ({ ...others }) => <Skeleton variant="rectangular" {...others} animation="wave" />;
|
||||
|
||||
export default ImagePlaceholder;
|
||||
155
web/berry/src/ui-component/cards/Skeleton/PopularCard.js
Normal file
155
web/berry/src/ui-component/cards/Skeleton/PopularCard.js
Normal file
@@ -0,0 +1,155 @@
|
||||
// material-ui
|
||||
import { Card, CardContent, Grid } from '@mui/material';
|
||||
import Skeleton from '@mui/material/Skeleton';
|
||||
|
||||
// project imports
|
||||
import { gridSpacing } from 'store/constant';
|
||||
|
||||
// ==============================|| SKELETON - POPULAR CARD ||============================== //
|
||||
|
||||
const PopularCard = () => (
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Grid container spacing={gridSpacing}>
|
||||
<Grid item xs={12}>
|
||||
<Grid container alignItems="center" justifyContent="space-between" spacing={gridSpacing}>
|
||||
<Grid item xs zeroMinWidth>
|
||||
<Skeleton variant="rectangular" height={20} />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Skeleton variant="rectangular" height={20} width={20} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Skeleton variant="rectangular" height={150} />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12}>
|
||||
<Grid container alignItems="center" spacing={gridSpacing} justifyContent="space-between">
|
||||
<Grid item xs={6}>
|
||||
<Skeleton variant="rectangular" height={20} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Grid container alignItems="center" spacing={gridSpacing} justifyContent="space-between">
|
||||
<Grid item xs zeroMinWidth>
|
||||
<Skeleton variant="rectangular" height={20} />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Skeleton variant="rectangular" height={16} width={16} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Skeleton variant="rectangular" height={20} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12}>
|
||||
<Grid container alignItems="center" spacing={gridSpacing} justifyContent="space-between">
|
||||
<Grid item xs={6}>
|
||||
<Skeleton variant="rectangular" height={20} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Grid container alignItems="center" spacing={gridSpacing} justifyContent="space-between">
|
||||
<Grid item xs zeroMinWidth>
|
||||
<Skeleton variant="rectangular" height={20} />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Skeleton variant="rectangular" height={16} width={16} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Skeleton variant="rectangular" height={20} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12}>
|
||||
<Grid container alignItems="center" spacing={gridSpacing} justifyContent="space-between">
|
||||
<Grid item xs={6}>
|
||||
<Skeleton variant="rectangular" height={20} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Grid container alignItems="center" spacing={gridSpacing} justifyContent="space-between">
|
||||
<Grid item xs zeroMinWidth>
|
||||
<Skeleton variant="rectangular" height={20} />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Skeleton variant="rectangular" height={16} width={16} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Skeleton variant="rectangular" height={20} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12}>
|
||||
<Grid container alignItems="center" spacing={gridSpacing} justifyContent="space-between">
|
||||
<Grid item xs={6}>
|
||||
<Skeleton variant="rectangular" height={20} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Grid container alignItems="center" spacing={gridSpacing} justifyContent="space-between">
|
||||
<Grid item xs zeroMinWidth>
|
||||
<Skeleton variant="rectangular" height={20} />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Skeleton variant="rectangular" height={16} width={16} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Skeleton variant="rectangular" height={20} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12}>
|
||||
<Grid container alignItems="center" spacing={gridSpacing} justifyContent="space-between">
|
||||
<Grid item xs={6}>
|
||||
<Skeleton variant="rectangular" height={20} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Grid container alignItems="center" spacing={gridSpacing} justifyContent="space-between">
|
||||
<Grid item xs zeroMinWidth>
|
||||
<Skeleton variant="rectangular" height={20} />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Skeleton variant="rectangular" height={16} width={16} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Skeleton variant="rectangular" height={20} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
<CardContent sx={{ p: 1.25, display: 'flex', pt: 0, justifyContent: 'center' }}>
|
||||
<Skeleton variant="rectangular" height={25} width={75} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
export default PopularCard;
|
||||
@@ -0,0 +1,44 @@
|
||||
// material-ui
|
||||
import { CardContent, Grid, Skeleton, Stack } from '@mui/material';
|
||||
|
||||
// project import
|
||||
import MainCard from '../MainCard';
|
||||
|
||||
// ===========================|| SKELETON TOTAL GROWTH BAR CHART ||=========================== //
|
||||
|
||||
const ProductPlaceholder = () => (
|
||||
<MainCard content={false} boxShadow>
|
||||
<Skeleton variant="rectangular" height={220} />
|
||||
<CardContent sx={{ p: 2 }}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Skeleton variant="rectangular" height={20} />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Skeleton variant="rectangular" height={45} />
|
||||
</Grid>
|
||||
<Grid item xs={12} sx={{ pt: '8px !important' }}>
|
||||
<Stack direction="row" alignItems="center" spacing={1}>
|
||||
<Skeleton variant="rectangular" height={20} width={90} />
|
||||
<Skeleton variant="rectangular" height={20} width={38} />
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Grid container spacing={1}>
|
||||
<Grid item>
|
||||
<Skeleton variant="rectangular" height={20} width={40} />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Skeleton variant="rectangular" height={17} width={20} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Skeleton variant="rectangular" height={32} width={47} />
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
</MainCard>
|
||||
);
|
||||
|
||||
export default ProductPlaceholder;
|
||||
@@ -0,0 +1,39 @@
|
||||
// material-ui
|
||||
import { Card, CardContent, Grid } from '@mui/material';
|
||||
import Skeleton from '@mui/material/Skeleton';
|
||||
|
||||
// project imports
|
||||
import { gridSpacing } from 'store/constant';
|
||||
|
||||
// ==============================|| SKELETON TOTAL GROWTH BAR CHART ||============================== //
|
||||
|
||||
const TotalGrowthBarChart = () => (
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Grid container spacing={gridSpacing}>
|
||||
<Grid item xs={12}>
|
||||
<Grid container alignItems="center" justifyContent="space-between" spacing={gridSpacing}>
|
||||
<Grid item xs zeroMinWidth>
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12}>
|
||||
<Skeleton variant="text" />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Skeleton variant="rectangular" height={20} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Skeleton variant="rectangular" height={50} width={80} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Skeleton variant="rectangular" height={530} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
export default TotalGrowthBarChart;
|
||||
19
web/berry/src/ui-component/cards/Skeleton/TotalIncomeCard.js
Normal file
19
web/berry/src/ui-component/cards/Skeleton/TotalIncomeCard.js
Normal file
@@ -0,0 +1,19 @@
|
||||
// material-ui
|
||||
import { Card, List, ListItem, ListItemAvatar, ListItemText, Skeleton } from '@mui/material';
|
||||
|
||||
// ==============================|| SKELETON - TOTAL INCOME DARK/LIGHT CARD ||============================== //
|
||||
|
||||
const TotalIncomeCard = () => (
|
||||
<Card sx={{ p: 2 }}>
|
||||
<List sx={{ py: 0 }}>
|
||||
<ListItem alignItems="center" disableGutters sx={{ py: 0 }}>
|
||||
<ListItemAvatar>
|
||||
<Skeleton variant="rectangular" width={44} height={44} />
|
||||
</ListItemAvatar>
|
||||
<ListItemText sx={{ py: 0 }} primary={<Skeleton variant="rectangular" height={20} />} secondary={<Skeleton variant="text" />} />
|
||||
</ListItem>
|
||||
</List>
|
||||
</Card>
|
||||
);
|
||||
|
||||
export default TotalIncomeCard;
|
||||
72
web/berry/src/ui-component/cards/SubCard.js
Normal file
72
web/berry/src/ui-component/cards/SubCard.js
Normal file
@@ -0,0 +1,72 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { Card, CardContent, CardHeader, Divider, Typography } from '@mui/material';
|
||||
|
||||
// ==============================|| CUSTOM SUB CARD ||============================== //
|
||||
|
||||
const SubCard = forwardRef(
|
||||
({ children, content, contentClass, darkTitle, secondary, sx = {}, contentSX = {}, title, subTitle, ...others }, ref) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Card
|
||||
ref={ref}
|
||||
sx={{
|
||||
border: '1px solid',
|
||||
borderColor: theme.palette.primary.light,
|
||||
':hover': {
|
||||
boxShadow: '0 2px 14px 0 rgb(32 40 45 / 8%)'
|
||||
},
|
||||
...sx
|
||||
}}
|
||||
{...others}
|
||||
>
|
||||
{/* card header and action */}
|
||||
{!darkTitle && title && (
|
||||
<CardHeader sx={{ p: 2.5 }} title={<Typography variant="h5">{title}</Typography>} action={secondary} subheader={subTitle} />
|
||||
)}
|
||||
{darkTitle && title && (
|
||||
<CardHeader sx={{ p: 2.5 }} title={<Typography variant="h4">{title}</Typography>} action={secondary} subheader={subTitle} />
|
||||
)}
|
||||
|
||||
{/* content & header divider */}
|
||||
{title && (
|
||||
<Divider
|
||||
sx={{
|
||||
opacity: 1,
|
||||
borderColor: theme.palette.primary.light
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* card content */}
|
||||
{content && (
|
||||
<CardContent sx={{ p: 2.5, ...contentSX }} className={contentClass || ''}>
|
||||
{children}
|
||||
</CardContent>
|
||||
)}
|
||||
{!content && children}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
SubCard.propTypes = {
|
||||
children: PropTypes.node,
|
||||
content: PropTypes.bool,
|
||||
contentClass: PropTypes.string,
|
||||
darkTitle: PropTypes.bool,
|
||||
secondary: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object]),
|
||||
sx: PropTypes.object,
|
||||
contentSX: PropTypes.object,
|
||||
title: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object])
|
||||
};
|
||||
|
||||
SubCard.defaultProps = {
|
||||
content: true
|
||||
};
|
||||
|
||||
export default SubCard;
|
||||
121
web/berry/src/ui-component/cards/UserCard.js
Normal file
121
web/berry/src/ui-component/cards/UserCard.js
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* UserCard.js
|
||||
*
|
||||
* This file uses code from the Minimal UI project, available at
|
||||
* https://github.com/minimal-ui-kit/material-kit-react/blob/main/src/sections/blog/post-card.jsx
|
||||
*
|
||||
* Minimal UI is licensed under the MIT License. A copy of the license is included below:
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021 Minimal UI (https://minimals.cc/)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import { Box, Avatar } from '@mui/material';
|
||||
import { alpha } from '@mui/material/styles';
|
||||
import Card from '@mui/material/Card';
|
||||
import shapeAvatar from 'assets/images/icons/shape-avatar.svg';
|
||||
import coverAvatar from 'assets/images/invite/cover.jpg';
|
||||
import userAvatar from 'assets/images/users/user-round.svg';
|
||||
import SvgColor from 'ui-component/SvgColor';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export default function UserCard({ children }) {
|
||||
const renderShape = (
|
||||
<SvgColor
|
||||
color="paper"
|
||||
src={shapeAvatar}
|
||||
sx={{
|
||||
width: '100%',
|
||||
height: 62,
|
||||
zIndex: 10,
|
||||
bottom: -26,
|
||||
position: 'absolute',
|
||||
color: 'background.paper'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const renderAvatar = (
|
||||
<Avatar
|
||||
src={userAvatar}
|
||||
sx={{
|
||||
zIndex: 11,
|
||||
width: 64,
|
||||
height: 64,
|
||||
position: 'absolute',
|
||||
alignItems: 'center',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: (theme) => theme.spacing(-4)
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const renderCover = (
|
||||
<Box
|
||||
component="img"
|
||||
src={coverAvatar}
|
||||
sx={{
|
||||
top: 0,
|
||||
width: 1,
|
||||
height: 1,
|
||||
objectFit: 'cover',
|
||||
position: 'absolute'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
'&:after': {
|
||||
top: 0,
|
||||
content: "''",
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
bgcolor: (theme) => alpha(theme.palette.primary.main, 0.42)
|
||||
},
|
||||
pt: {
|
||||
xs: 'calc(100% / 3)',
|
||||
sm: 'calc(100% / 4.66)'
|
||||
}
|
||||
}}
|
||||
>
|
||||
{renderShape}
|
||||
{renderAvatar}
|
||||
{renderCover}
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
p: (theme) => theme.spacing(4, 3, 3, 3)
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
92
web/berry/src/ui-component/extended/AnimateButton.js
Normal file
92
web/berry/src/ui-component/extended/AnimateButton.js
Normal file
@@ -0,0 +1,92 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { forwardRef } from 'react';
|
||||
// third-party
|
||||
import { motion, useCycle } from 'framer-motion';
|
||||
|
||||
// ==============================|| ANIMATION BUTTON ||============================== //
|
||||
|
||||
const AnimateButton = forwardRef(({ children, type, direction, offset, scale }, ref) => {
|
||||
let offset1;
|
||||
let offset2;
|
||||
switch (direction) {
|
||||
case 'up':
|
||||
case 'left':
|
||||
offset1 = offset;
|
||||
offset2 = 0;
|
||||
break;
|
||||
case 'right':
|
||||
case 'down':
|
||||
default:
|
||||
offset1 = 0;
|
||||
offset2 = offset;
|
||||
break;
|
||||
}
|
||||
|
||||
const [x, cycleX] = useCycle(offset1, offset2);
|
||||
const [y, cycleY] = useCycle(offset1, offset2);
|
||||
|
||||
switch (type) {
|
||||
case 'rotate':
|
||||
return (
|
||||
<motion.div
|
||||
ref={ref}
|
||||
animate={{ rotate: 360 }}
|
||||
transition={{
|
||||
repeat: Infinity,
|
||||
repeatType: 'loop',
|
||||
duration: 2,
|
||||
repeatDelay: 0
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
case 'slide':
|
||||
if (direction === 'up' || direction === 'down') {
|
||||
return (
|
||||
<motion.div ref={ref} animate={{ y: y !== undefined ? y : '' }} onHoverEnd={() => cycleY()} onHoverStart={() => cycleY()}>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<motion.div ref={ref} animate={{ x: x !== undefined ? x : '' }} onHoverEnd={() => cycleX()} onHoverStart={() => cycleX()}>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
case 'scale':
|
||||
default:
|
||||
if (typeof scale === 'number') {
|
||||
scale = {
|
||||
hover: scale,
|
||||
tap: scale
|
||||
};
|
||||
}
|
||||
return (
|
||||
<motion.div ref={ref} whileHover={{ scale: scale?.hover }} whileTap={{ scale: scale?.tap }}>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
AnimateButton.propTypes = {
|
||||
children: PropTypes.node,
|
||||
offset: PropTypes.number,
|
||||
type: PropTypes.oneOf(['slide', 'scale', 'rotate']),
|
||||
direction: PropTypes.oneOf(['up', 'down', 'left', 'right']),
|
||||
scale: PropTypes.oneOfType([PropTypes.number, PropTypes.object])
|
||||
};
|
||||
|
||||
AnimateButton.defaultProps = {
|
||||
type: 'scale',
|
||||
offset: 10,
|
||||
direction: 'right',
|
||||
scale: {
|
||||
hover: 1,
|
||||
tap: 0.9
|
||||
}
|
||||
};
|
||||
|
||||
export default AnimateButton;
|
||||
72
web/berry/src/ui-component/extended/Avatar.js
Normal file
72
web/berry/src/ui-component/extended/Avatar.js
Normal file
@@ -0,0 +1,72 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import MuiAvatar from '@mui/material/Avatar';
|
||||
|
||||
// ==============================|| AVATAR ||============================== //
|
||||
|
||||
const Avatar = ({ color, outline, size, sx, ...others }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const colorSX = color && !outline && { color: theme.palette.background.paper, bgcolor: `${color}.main` };
|
||||
const outlineSX = outline && {
|
||||
color: color ? `${color}.main` : `primary.main`,
|
||||
bgcolor: theme.palette.background.paper,
|
||||
border: '2px solid',
|
||||
borderColor: color ? `${color}.main` : `primary.main`
|
||||
};
|
||||
let sizeSX = {};
|
||||
switch (size) {
|
||||
case 'badge':
|
||||
sizeSX = {
|
||||
width: theme.spacing(3.5),
|
||||
height: theme.spacing(3.5)
|
||||
};
|
||||
break;
|
||||
case 'xs':
|
||||
sizeSX = {
|
||||
width: theme.spacing(4.25),
|
||||
height: theme.spacing(4.25)
|
||||
};
|
||||
break;
|
||||
case 'sm':
|
||||
sizeSX = {
|
||||
width: theme.spacing(5),
|
||||
height: theme.spacing(5)
|
||||
};
|
||||
break;
|
||||
case 'lg':
|
||||
sizeSX = {
|
||||
width: theme.spacing(9),
|
||||
height: theme.spacing(9)
|
||||
};
|
||||
break;
|
||||
case 'xl':
|
||||
sizeSX = {
|
||||
width: theme.spacing(10.25),
|
||||
height: theme.spacing(10.25)
|
||||
};
|
||||
break;
|
||||
case 'md':
|
||||
sizeSX = {
|
||||
width: theme.spacing(7.5),
|
||||
height: theme.spacing(7.5)
|
||||
};
|
||||
break;
|
||||
default:
|
||||
sizeSX = {};
|
||||
}
|
||||
|
||||
return <MuiAvatar sx={{ ...colorSX, ...outlineSX, ...sizeSX, ...sx }} {...others} />;
|
||||
};
|
||||
|
||||
Avatar.propTypes = {
|
||||
className: PropTypes.string,
|
||||
color: PropTypes.string,
|
||||
outline: PropTypes.bool,
|
||||
size: PropTypes.string,
|
||||
sx: PropTypes.object
|
||||
};
|
||||
|
||||
export default Avatar;
|
||||
187
web/berry/src/ui-component/extended/Breadcrumbs.js
Normal file
187
web/berry/src/ui-component/extended/Breadcrumbs.js
Normal file
@@ -0,0 +1,187 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { Box, Card, Divider, Grid, Typography } from '@mui/material';
|
||||
import MuiBreadcrumbs from '@mui/material/Breadcrumbs';
|
||||
|
||||
// project imports
|
||||
import config from 'config';
|
||||
import { gridSpacing } from 'store/constant';
|
||||
|
||||
// assets
|
||||
import { IconTallymark1 } from '@tabler/icons-react';
|
||||
import AccountTreeTwoToneIcon from '@mui/icons-material/AccountTreeTwoTone';
|
||||
import HomeIcon from '@mui/icons-material/Home';
|
||||
import HomeTwoToneIcon from '@mui/icons-material/HomeTwoTone';
|
||||
|
||||
const linkSX = {
|
||||
display: 'flex',
|
||||
color: 'grey.900',
|
||||
textDecoration: 'none',
|
||||
alignContent: 'center',
|
||||
alignItems: 'center'
|
||||
};
|
||||
|
||||
// ==============================|| BREADCRUMBS ||============================== //
|
||||
|
||||
const Breadcrumbs = ({ card, divider, icon, icons, maxItems, navigation, rightAlign, separator, title, titleBottom, ...others }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const iconStyle = {
|
||||
marginRight: theme.spacing(0.75),
|
||||
marginTop: `-${theme.spacing(0.25)}`,
|
||||
width: '1rem',
|
||||
height: '1rem',
|
||||
color: theme.palette.secondary.main
|
||||
};
|
||||
|
||||
const [main, setMain] = useState();
|
||||
const [item, setItem] = useState();
|
||||
|
||||
// set active item state
|
||||
const getCollapse = (menu) => {
|
||||
if (menu.children) {
|
||||
menu.children.filter((collapse) => {
|
||||
if (collapse.type && collapse.type === 'collapse') {
|
||||
getCollapse(collapse);
|
||||
} else if (collapse.type && collapse.type === 'item') {
|
||||
if (document.location.pathname === config.basename + collapse.url) {
|
||||
setMain(menu);
|
||||
setItem(collapse);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
navigation?.items?.map((menu) => {
|
||||
if (menu.type && menu.type === 'group') {
|
||||
getCollapse(menu);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
// item separator
|
||||
const SeparatorIcon = separator;
|
||||
const separatorIcon = separator ? <SeparatorIcon stroke={1.5} size="1rem" /> : <IconTallymark1 stroke={1.5} size="1rem" />;
|
||||
|
||||
let mainContent;
|
||||
let itemContent;
|
||||
let breadcrumbContent = <Typography />;
|
||||
let itemTitle = '';
|
||||
let CollapseIcon;
|
||||
let ItemIcon;
|
||||
|
||||
// collapse item
|
||||
if (main && main.type === 'collapse') {
|
||||
CollapseIcon = main.icon ? main.icon : AccountTreeTwoToneIcon;
|
||||
mainContent = (
|
||||
<Typography component={Link} to="#" variant="subtitle1" sx={linkSX}>
|
||||
{icons && <CollapseIcon style={iconStyle} />}
|
||||
{main.title}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
// items
|
||||
if (item && item.type === 'item') {
|
||||
itemTitle = item.title;
|
||||
|
||||
ItemIcon = item.icon ? item.icon : AccountTreeTwoToneIcon;
|
||||
itemContent = (
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
textDecoration: 'none',
|
||||
alignContent: 'center',
|
||||
alignItems: 'center',
|
||||
color: 'grey.500'
|
||||
}}
|
||||
>
|
||||
{icons && <ItemIcon style={iconStyle} />}
|
||||
{itemTitle}
|
||||
</Typography>
|
||||
);
|
||||
|
||||
// main
|
||||
if (item.breadcrumbs !== false) {
|
||||
breadcrumbContent = (
|
||||
<Card
|
||||
sx={{
|
||||
marginBottom: card === false ? 0 : theme.spacing(gridSpacing),
|
||||
border: card === false ? 'none' : '1px solid',
|
||||
borderColor: theme.palette.primary[200] + 75,
|
||||
background: card === false ? 'transparent' : theme.palette.background.default
|
||||
}}
|
||||
{...others}
|
||||
>
|
||||
<Box sx={{ p: 2, pl: card === false ? 0 : 2 }}>
|
||||
<Grid
|
||||
container
|
||||
direction={rightAlign ? 'row' : 'column'}
|
||||
justifyContent={rightAlign ? 'space-between' : 'flex-start'}
|
||||
alignItems={rightAlign ? 'center' : 'flex-start'}
|
||||
spacing={1}
|
||||
>
|
||||
{title && !titleBottom && (
|
||||
<Grid item>
|
||||
<Typography variant="h3" sx={{ fontWeight: 500 }}>
|
||||
{item.title}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item>
|
||||
<MuiBreadcrumbs
|
||||
sx={{ '& .MuiBreadcrumbs-separator': { width: 16, ml: 1.25, mr: 1.25 } }}
|
||||
aria-label="breadcrumb"
|
||||
maxItems={maxItems || 8}
|
||||
separator={separatorIcon}
|
||||
>
|
||||
<Typography component={Link} to="/" color="inherit" variant="subtitle1" sx={linkSX}>
|
||||
{icons && <HomeTwoToneIcon sx={iconStyle} />}
|
||||
{icon && <HomeIcon sx={{ ...iconStyle, mr: 0 }} />}
|
||||
{!icon && 'Dashboard'}
|
||||
</Typography>
|
||||
{mainContent}
|
||||
{itemContent}
|
||||
</MuiBreadcrumbs>
|
||||
</Grid>
|
||||
{title && titleBottom && (
|
||||
<Grid item>
|
||||
<Typography variant="h3" sx={{ fontWeight: 500 }}>
|
||||
{item.title}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Box>
|
||||
{card === false && divider !== false && <Divider sx={{ borderColor: theme.palette.primary.main, mb: gridSpacing }} />}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return breadcrumbContent;
|
||||
};
|
||||
|
||||
Breadcrumbs.propTypes = {
|
||||
card: PropTypes.bool,
|
||||
divider: PropTypes.bool,
|
||||
icon: PropTypes.bool,
|
||||
icons: PropTypes.bool,
|
||||
maxItems: PropTypes.number,
|
||||
navigation: PropTypes.object,
|
||||
rightAlign: PropTypes.bool,
|
||||
separator: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
|
||||
title: PropTypes.bool,
|
||||
titleBottom: PropTypes.bool
|
||||
};
|
||||
|
||||
export default Breadcrumbs;
|
||||
107
web/berry/src/ui-component/extended/Transitions.js
Normal file
107
web/berry/src/ui-component/extended/Transitions.js
Normal file
@@ -0,0 +1,107 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
// material-ui
|
||||
import { Collapse, Fade, Box, Grow, Slide, Zoom } from '@mui/material';
|
||||
|
||||
// ==============================|| TRANSITIONS ||============================== //
|
||||
|
||||
const Transitions = forwardRef(({ children, position, type, direction, ...others }, ref) => {
|
||||
let positionSX = {
|
||||
transformOrigin: '0 0 0'
|
||||
};
|
||||
|
||||
switch (position) {
|
||||
case 'top-right':
|
||||
positionSX = {
|
||||
transformOrigin: 'top right'
|
||||
};
|
||||
break;
|
||||
case 'top':
|
||||
positionSX = {
|
||||
transformOrigin: 'top'
|
||||
};
|
||||
break;
|
||||
case 'bottom-left':
|
||||
positionSX = {
|
||||
transformOrigin: 'bottom left'
|
||||
};
|
||||
break;
|
||||
case 'bottom-right':
|
||||
positionSX = {
|
||||
transformOrigin: 'bottom right'
|
||||
};
|
||||
break;
|
||||
case 'bottom':
|
||||
positionSX = {
|
||||
transformOrigin: 'bottom'
|
||||
};
|
||||
break;
|
||||
case 'top-left':
|
||||
default:
|
||||
positionSX = {
|
||||
transformOrigin: '0 0 0'
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box ref={ref}>
|
||||
{type === 'grow' && (
|
||||
<Grow {...others}>
|
||||
<Box sx={positionSX}>{children}</Box>
|
||||
</Grow>
|
||||
)}
|
||||
{type === 'collapse' && (
|
||||
<Collapse {...others} sx={positionSX}>
|
||||
{children}
|
||||
</Collapse>
|
||||
)}
|
||||
{type === 'fade' && (
|
||||
<Fade
|
||||
{...others}
|
||||
timeout={{
|
||||
appear: 500,
|
||||
enter: 600,
|
||||
exit: 400
|
||||
}}
|
||||
>
|
||||
<Box sx={positionSX}>{children}</Box>
|
||||
</Fade>
|
||||
)}
|
||||
{type === 'slide' && (
|
||||
<Slide
|
||||
{...others}
|
||||
timeout={{
|
||||
appear: 0,
|
||||
enter: 400,
|
||||
exit: 200
|
||||
}}
|
||||
direction={direction}
|
||||
>
|
||||
<Box sx={positionSX}>{children}</Box>
|
||||
</Slide>
|
||||
)}
|
||||
{type === 'zoom' && (
|
||||
<Zoom {...others}>
|
||||
<Box sx={positionSX}>{children}</Box>
|
||||
</Zoom>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
|
||||
Transitions.propTypes = {
|
||||
children: PropTypes.node,
|
||||
type: PropTypes.oneOf(['grow', 'fade', 'collapse', 'slide', 'zoom']),
|
||||
position: PropTypes.oneOf(['top-left', 'top-right', 'top', 'bottom-left', 'bottom-right', 'bottom']),
|
||||
direction: PropTypes.oneOf(['up', 'down', 'left', 'right'])
|
||||
};
|
||||
|
||||
Transitions.defaultProps = {
|
||||
type: 'grow',
|
||||
position: 'top-left',
|
||||
direction: 'up'
|
||||
};
|
||||
|
||||
export default Transitions;
|
||||
Reference in New Issue
Block a user