Manspof
Manspof

Reputation: 357

Material ui TreeItems change style conditionally based

I'm using TreeView, TreeItem of material ui. I'm trying to style conditionally each node (selected, hover, focus). actually what I'm trying to do is that only node that not disabledButton (props that come from outside) will be in right color. only node that selectable will be in active color (expand node not means that node is selectable). something like this example enter image description here

I did this example, but the behavior is really weird, sometimes the "child-4" looks like the style of disabledButton even I sent the right props, maybe because it nested props, sometimes the other nested not get the right colors of the hover, focus and selected.. link for example (with colors) example with different colors

I want it to be in real colors also with the theme (active should be like blue color, hover gray) so I created this example. example with theme colors sometimes it works as expected and sometimes is not. I looked in this issue but it's not like my example but there it uses in classes but I don't want to send this way

Upvotes: 0

Views: 1947

Answers (1)

sm3sher
sm3sher

Reputation: 3360

After a little while this is what I got. I tried to match the design of the gif as good as possible.

enter image description here

I used this data which I derived from the question. This example has one disabled node and parent node (my-photo.jpg and zip directory). The data array can be expanded endlessly if it matches TreeData type.

export type TreeData = {
  id: string;
  name: string;
  disabledButton: boolean;
  children?: TreeData[];
};

const data: TreeData[] = [
  {
    id: "1",
    name: "My Web Site",
    disabledButton: false,
    children: [
      {
        id: "2",
        name: "images",
        disabledButton: false,
        children: [
          { id: "3", name: "logo.png", disabledButton: false },
          { id: "4", name: "body-back.png", disabledButton: false },
          { id: "5", name: "my-photo.jpg", disabledButton: true }
        ]
      },
      {
        id: "6",
        name: "resources",
        disabledButton: false,
        children: [
          {
            id: "7",
            name: "pdf",
            disabledButton: false,
            children: [
              { id: "8", name: "brochure.pdf", disabledButton: false },
              { id: "9", name: "prices.pdf", disabledButton: false }
            ]
          },
          {
            id: "10",
            name: "zip",
            disabledButton: true,
            children: [{ id: "11", name: "test.zip", disabledButton: false }]
          }
        ]
      }
    ]
  }
];

The CustomTreeView consists of CustomTreeItem that again makes use of CustomContent. CustomContent is used to handle all events and to display another icon (folder) next to the expandIcon.

In the CustomTreeItem I set the width to fit-content to not select the whole row, but to match the example from the gif:

const CustomTreeItem = (props: TreeItemProps) => (
  <TreeItem
    ContentComponent={CustomContent}
    {...props}
    sx={{
      width: "fit-content",
      ".MuiTreeItem-content": {
        py: "2px",
        width: "fit-content"
      }
    }}
  />
);

The interesting part is the styling of the CustomTreeView and its usage. I have packed the classes into objects, which can be overwritten easily. The theming happens in the styles class which is in styles/Styles.ts.

// Styles.ts
import { createTheme } from "@mui/material";

export const getMuiTheme = () =>
  createTheme({
    palette: {
      primary: {
        main: "#2160dd"
      },
      secondary: {
        main: "#d3d3d3"
      }
    }
  });

// CustomTreeView.tsx
const classes = {
  focused: {
    bgcolor: "transparent",
    py: "1px",
    px: "7px",
    border: `1px solid ${getMuiTheme().palette.secondary.main}`
  },
  selected: {
    bgcolor: getMuiTheme().palette.primary.main,
    color: "white"
  }
};

const CustomTreeView = ({ data }: { data: TreeData[] }) => {
  return (
    <Box mt={2} ml={2} bgcolor="white" width="300px">
      <ThemeProvider theme={getMuiTheme()}>
        <TreeView
          defaultCollapseIcon={<ArrowDropDownIcon />}
          defaultExpandIcon={<ArrowRightIcon />}
          defaultEndIcon={<InsertDriveFile />}
          sx={{
            ".MuiTreeItem-root": {
              ".Mui-focused:not(.Mui-selected)": classes.focused,
              ".Mui-selected, .Mui-focused.Mui-selected, .Mui-selected:hover":
                classes.selected
            }
          }}
        >
          {renderTreeData(data)}
        </TreeView>
      </ThemeProvider>
    </Box>
  );
};

To render arbitrarily nested data we make use of the recursive method renderTreeData. This way the data array can be expanded endlessly as long as it matches TreeData type.

export const renderTreeData = (data: TreeData[]) => {
  return data.map((item) => (
    <React.Fragment key={item.id}>
      {item.children ? (
        <CustomTreeItem
          nodeId={item.id}
          label={item.name}
          disabled={item.disabledButton}
          icon={<FolderOutlined />}
        >
          {renderTreeData(item.children)}
        </CustomTreeItem>
      ) : (
        <CustomTreeItem
          nodeId={item.id}
          label={item.name}
          disabled={item.disabledButton}
          icon={getFileIcon(item.name)}
        />
      )}
    </React.Fragment>
  ));
};

Live Demo

Edit nervous-babycat-jlbqnq

Upvotes: 2

Related Questions