Some checks failed
No response / noResponse (push) Has been cancelled
CI / Continuous releases (push) Has been cancelled
CI / test-dev (macos-latest) (push) Has been cancelled
CI / test-dev (ubuntu-latest) (push) Has been cancelled
CI / test-dev (windows-latest) (push) Has been cancelled
Maintenance / main (push) Has been cancelled
Scorecards supply-chain security / Scorecards analysis (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
228 lines
5.9 KiB
JavaScript
228 lines
5.9 KiB
JavaScript
import * as React from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import clsx from 'clsx';
|
|
import { animated, useSpring } from '@react-spring/web';
|
|
|
|
import Box from '@mui/material/Box';
|
|
import Card from '@mui/material/Card';
|
|
import CardContent from '@mui/material/CardContent';
|
|
import Collapse from '@mui/material/Collapse';
|
|
import Typography from '@mui/material/Typography';
|
|
import { RichTreeView } from '@mui/x-tree-view/RichTreeView';
|
|
import { useTreeItem } from '@mui/x-tree-view/useTreeItem';
|
|
import {
|
|
TreeItemContent,
|
|
TreeItemIconContainer,
|
|
TreeItemLabel,
|
|
TreeItemRoot,
|
|
} from '@mui/x-tree-view/TreeItem';
|
|
import { TreeItemIcon } from '@mui/x-tree-view/TreeItemIcon';
|
|
import { TreeItemProvider } from '@mui/x-tree-view/TreeItemProvider';
|
|
|
|
import { useTheme } from '@mui/material/styles';
|
|
|
|
const ITEMS = [
|
|
{
|
|
id: '1',
|
|
label: 'Website',
|
|
children: [
|
|
{ id: '1.1', label: 'Home', color: 'green' },
|
|
{ id: '1.2', label: 'Pricing', color: 'green' },
|
|
{ id: '1.3', label: 'About us', color: 'green' },
|
|
{
|
|
id: '1.4',
|
|
label: 'Blog',
|
|
children: [
|
|
{ id: '1.1.1', label: 'Announcements', color: 'blue' },
|
|
{ id: '1.1.2', label: 'April lookahead', color: 'blue' },
|
|
{ id: '1.1.3', label: "What's new", color: 'blue' },
|
|
{ id: '1.1.4', label: 'Meet the team', color: 'blue' },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: '2',
|
|
label: 'Store',
|
|
children: [
|
|
{ id: '2.1', label: 'All products', color: 'green' },
|
|
{
|
|
id: '2.2',
|
|
label: 'Categories',
|
|
children: [
|
|
{ id: '2.2.1', label: 'Gadgets', color: 'blue' },
|
|
{ id: '2.2.2', label: 'Phones', color: 'blue' },
|
|
{ id: '2.2.3', label: 'Wearables', color: 'blue' },
|
|
],
|
|
},
|
|
{ id: '2.3', label: 'Bestsellers', color: 'green' },
|
|
{ id: '2.4', label: 'Sales', color: 'green' },
|
|
],
|
|
},
|
|
{ id: '4', label: 'Contact', color: 'blue' },
|
|
{ id: '5', label: 'Help', color: 'blue' },
|
|
];
|
|
|
|
function DotIcon({ color }) {
|
|
return (
|
|
<Box sx={{ marginRight: 1, display: 'flex', alignItems: 'center' }}>
|
|
<svg width={6} height={6}>
|
|
<circle cx={3} cy={3} r={3} fill={color} />
|
|
</svg>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
DotIcon.propTypes = {
|
|
color: PropTypes.string.isRequired,
|
|
};
|
|
|
|
const AnimatedCollapse = animated(Collapse);
|
|
|
|
function TransitionComponent(props) {
|
|
const style = useSpring({
|
|
to: {
|
|
opacity: props.in ? 1 : 0,
|
|
transform: `translate3d(0,${props.in ? 0 : 20}px,0)`,
|
|
},
|
|
});
|
|
|
|
return <AnimatedCollapse style={style} {...props} />;
|
|
}
|
|
|
|
TransitionComponent.propTypes = {
|
|
/**
|
|
* Show the component; triggers the enter or exit states
|
|
*/
|
|
in: PropTypes.bool,
|
|
};
|
|
|
|
function CustomLabel({ color, expandable, children, ...other }) {
|
|
const theme = useTheme();
|
|
const colors = {
|
|
blue: (theme.vars || theme).palette.primary.main,
|
|
green: (theme.vars || theme).palette.success.main,
|
|
};
|
|
|
|
const iconColor = color ? colors[color] : null;
|
|
return (
|
|
<TreeItemLabel {...other} sx={{ display: 'flex', alignItems: 'center' }}>
|
|
{iconColor && <DotIcon color={iconColor} />}
|
|
<Typography
|
|
className="labelText"
|
|
variant="body2"
|
|
sx={{ color: 'text.primary' }}
|
|
>
|
|
{children}
|
|
</Typography>
|
|
</TreeItemLabel>
|
|
);
|
|
}
|
|
|
|
CustomLabel.propTypes = {
|
|
children: PropTypes.node,
|
|
color: PropTypes.oneOf(['blue', 'green']),
|
|
expandable: PropTypes.bool,
|
|
};
|
|
|
|
const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) {
|
|
const { id, itemId, label, disabled, children, ...other } = props;
|
|
|
|
const {
|
|
getRootProps,
|
|
getContentProps,
|
|
getIconContainerProps,
|
|
getLabelProps,
|
|
getGroupTransitionProps,
|
|
status,
|
|
publicAPI,
|
|
} = useTreeItem({ id, itemId, children, label, disabled, rootRef: ref });
|
|
|
|
const item = publicAPI.getItem(itemId);
|
|
const color = item?.color;
|
|
return (
|
|
<TreeItemProvider id={id} itemId={itemId}>
|
|
<TreeItemRoot {...getRootProps(other)}>
|
|
<TreeItemContent
|
|
{...getContentProps({
|
|
className: clsx('content', {
|
|
expanded: status.expanded,
|
|
selected: status.selected,
|
|
focused: status.focused,
|
|
disabled: status.disabled,
|
|
}),
|
|
})}
|
|
>
|
|
{status.expandable && (
|
|
<TreeItemIconContainer {...getIconContainerProps()}>
|
|
<TreeItemIcon status={status} />
|
|
</TreeItemIconContainer>
|
|
)}
|
|
|
|
<CustomLabel {...getLabelProps({ color })} />
|
|
</TreeItemContent>
|
|
{children && (
|
|
<TransitionComponent
|
|
{...getGroupTransitionProps({ className: 'groupTransition' })}
|
|
/>
|
|
)}
|
|
</TreeItemRoot>
|
|
</TreeItemProvider>
|
|
);
|
|
});
|
|
|
|
CustomTreeItem.propTypes = {
|
|
/**
|
|
* The content of the component.
|
|
*/
|
|
children: PropTypes.node,
|
|
/**
|
|
* If `true`, the item is disabled.
|
|
* @default false
|
|
*/
|
|
disabled: PropTypes.bool,
|
|
/**
|
|
* The id attribute of the item. If not provided, it will be generated.
|
|
*/
|
|
id: PropTypes.string,
|
|
/**
|
|
* The id of the item.
|
|
* Must be unique.
|
|
*/
|
|
itemId: PropTypes.string.isRequired,
|
|
/**
|
|
* The label of the item.
|
|
*/
|
|
label: PropTypes.node,
|
|
};
|
|
|
|
export default function CustomizedTreeView() {
|
|
return (
|
|
<Card
|
|
variant="outlined"
|
|
sx={{ display: 'flex', flexDirection: 'column', gap: '8px', flexGrow: 1 }}
|
|
>
|
|
<CardContent>
|
|
<Typography component="h2" variant="subtitle2">
|
|
Product tree
|
|
</Typography>
|
|
<RichTreeView
|
|
items={ITEMS}
|
|
aria-label="pages"
|
|
multiSelect
|
|
defaultExpandedItems={['1', '1.1']}
|
|
defaultSelectedItems={['1.1', '1.1.1']}
|
|
sx={{
|
|
m: '0 -8px',
|
|
pb: '8px',
|
|
height: 'fit-content',
|
|
flexGrow: 1,
|
|
overflowY: 'auto',
|
|
}}
|
|
slots={{ item: CustomTreeItem }}
|
|
/>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|