Files
react-test/docs/data/material/getting-started/templates/dashboard/components/CustomizedTreeView.tsx
how2ice 005cf56baf
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
init project
2025-12-12 14:26:25 +09:00

206 lines
5.7 KiB
TypeScript

import * as React from 'react';
import clsx from 'clsx';
import { animated, useSpring } from '@react-spring/web';
import { TransitionProps } from '@mui/material/transitions';
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, UseTreeItemParameters } 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 { TreeViewBaseItem } from '@mui/x-tree-view/models';
import { useTheme } from '@mui/material/styles';
type Color = 'blue' | 'green';
type ExtendedTreeItemProps = {
color?: Color;
id: string;
label: string;
};
const ITEMS: TreeViewBaseItem<ExtendedTreeItemProps>[] = [
{
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 }: { color: string }) {
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>
);
}
const AnimatedCollapse = animated(Collapse);
function TransitionComponent(props: TransitionProps) {
const style = useSpring({
to: {
opacity: props.in ? 1 : 0,
transform: `translate3d(0,${props.in ? 0 : 20}px,0)`,
},
});
return <AnimatedCollapse style={style} {...props} />;
}
interface CustomLabelProps {
children: React.ReactNode;
color?: Color;
expandable?: boolean;
}
function CustomLabel({ color, expandable, children, ...other }: CustomLabelProps) {
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>
);
}
interface CustomTreeItemProps
extends Omit<UseTreeItemParameters, 'rootRef'>,
Omit<React.HTMLAttributes<HTMLLIElement>, 'onFocus'> {}
const CustomTreeItem = React.forwardRef(function CustomTreeItem(
props: CustomTreeItemProps,
ref: React.Ref<HTMLLIElement>,
) {
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>
);
});
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>
);
}