简体   繁体   中英

Material-UI: classes isn't working with external SVG icons?

I have created a Material-UI persistent drawer in which there is a list item component that aims to change the icon color whenever a user clicks on the list item. But my styling is only working with Material-UI icon, not with external SVG.

Here is codesandbox link for the same project to understand it better.

Here is my AppBarDrawer.js parent component that renders my listItem component. Working fine and can be ignored

import React from "react";
import clsx from "clsx";
import { makeStyles, useTheme } from "@material-ui/core/styles";
import Drawer from "@material-ui/core/Drawer";
import CssBaseline from "@material-ui/core/CssBaseline";
import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar";
import List from "@material-ui/core/List";
import Typography from "@material-ui/core/Typography";
import Divider from "@material-ui/core/Divider";
import IconButton from "@material-ui/core/IconButton";
import MenuIcon from "@material-ui/icons/Menu";
import ChevronLeftIcon from "@material-ui/icons/ChevronLeft";
import ChevronRightIcon from "@material-ui/icons/ChevronRight";
import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import InboxIcon from "@material-ui/icons/MoveToInbox";
import MailIcon from "@material-ui/icons/Mail";
import DrawerList from "./components/DrawerList";

const drawerWidth = 240;

const useStyles = makeStyles((theme) => ({
  root: {
    display: "flex"
  },
  appBar: {
    transition: theme.transitions.create(["margin", "width"], {
      easing: theme.transitions.easing.sharp,
      duration: theme.transitions.duration.leavingScreen
    })
  },
  appBarShift: {
    width: `calc(100% - ${drawerWidth}px)`,
    marginLeft: drawerWidth,
    transition: theme.transitions.create(["margin", "width"], {
      easing: theme.transitions.easing.easeOut,
      duration: theme.transitions.duration.enteringScreen
    })
  },
  menuButton: {
    marginRight: theme.spacing(2)
  },
  hide: {
    display: "none"
  },
  drawer: {
    width: drawerWidth,
    flexShrink: 0
  },
  drawerPaper: {
    width: drawerWidth
  },
  drawerHeader: {
    display: "flex",
    alignItems: "center",
    padding: theme.spacing(0, 1),
    // necessary for content to be below app bar
    ...theme.mixins.toolbar,
    justifyContent: "flex-end"
  },
  content: {
    flexGrow: 1,
    padding: theme.spacing(3),
    transition: theme.transitions.create("margin", {
      easing: theme.transitions.easing.sharp,
      duration: theme.transitions.duration.leavingScreen
    }),
    marginLeft: -drawerWidth
  },
  contentShift: {
    transition: theme.transitions.create("margin", {
      easing: theme.transitions.easing.easeOut,
      duration: theme.transitions.duration.enteringScreen
    }),
    marginLeft: 0
  }
}));

export default function PersistentDrawerLeft() {
  const classes = useStyles();
  const theme = useTheme();
  const [open, setOpen] = React.useState(true);

  const handleDrawerOpen = () => {
    setOpen(true);
  };

  const handleDrawerClose = () => {
    setOpen(false);
  };

  return (
    <div className={classes.root}>
      <CssBaseline />
      <AppBar
        position="fixed"
        className={clsx(classes.appBar, {
          [classes.appBarShift]: open
        })}
      >
        <Toolbar>
          <IconButton
            color="inherit"
            aria-label="open drawer"
            onClick={handleDrawerOpen}
            edge="start"
            className={clsx(classes.menuButton, open && classes.hide)}
          >
            <MenuIcon />
          </IconButton>
          <Typography variant="h6" noWrap>
            Persistent drawer
          </Typography>
        </Toolbar>
      </AppBar>
      <Drawer
        className={classes.drawer}
        variant="persistent"
        anchor="left"
        open={open}
        classes={{
          paper: classes.drawerPaper
        }}
      >
        <div className={classes.drawerHeader}>
          <IconButton onClick={handleDrawerClose}>
            {theme.direction === "ltr" ? (
              <ChevronLeftIcon />
            ) : (
              <ChevronRightIcon />
            )}
          </IconButton>
        </div>
        <Divider />
        <List>
          <DrawerList />
        </List>
      </Drawer>
      <main
        className={clsx(classes.content, {
          [classes.contentShift]: open
        })}
      >
        <div className={classes.drawerHeader} />

        <Typography paragraph>
          Lorem Nulla posuere sollicitudin aliquam ultrices sagittis orci a
        </Typography>
      </main>
    </div>
  );
}

The Main file DrawerList.js which is not giving desired out

Here the real issue is my external icons color is not changing to white whenever I click on it however the last icon named ExitToAppOutlined is a Material-UI icon and is working fine on click.

import React, { useState } from "react";
import ListItem from "@material-ui/core/ListItem";
import Link from "@material-ui/core/Link";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import { ExitToAppOutlined } from "@material-ui/icons";
import ListItemText from "@material-ui/core/ListItemText";
import { useStyles } from "./DrawerListStyle";
import Typography from "@material-ui/core/Typography";
import Box from "@material-ui/core/Box";
import { SvgIcon } from "@material-ui/core";

import { ReactComponent as Appointment } from "../../assets/Appointment.svg";
import { ReactComponent as Customers } from "../../assets/manage customers 2.svg";

const itemList = [
  {
    text: "Book Appointment",
    icon: (
      <SvgIcon>
        {/* external icons as svg */}
        <Appointment />
      </SvgIcon>
    )
  },
  {
    text: "Manage",
    icon: (
      <SvgIcon>
        {/* external icons as svg */}
        <Customers />
      </SvgIcon>
    )
  },
  {
    text: "Logout",
    // Material Icons
    icon: <ExitToAppOutlined />
  }
];

const DrawerList = () => {
  const [selectedIndex, setSelectedIndex] = useState(0);
  const classes = useStyles();

  const ListData = () =>
    itemList.map((item, index) => {
      const { text, icon } = item;

      return (
        <ListItem
          button
          key={text}
          component={Link}
          selected={index === selectedIndex}
          onClick={(e) => handleListItemClick(e, index)}
          style={selectedIndex === index ? { backgroundColor: "#6A2CD8" } : {}}
        >
          <ListItemIcon
            className={classes.iconStyle}
            style={selectedIndex === index ? { color: "#fff" } : {}}
          >
            {icon}
            <ListItemText>
              <Typography
                component="div"
                className={classes.iconTitle}
                style={selectedIndex === index ? { color: "#fff" } : {}}
              >
                <Box fontWeight={500} fontSize={13.5}>
                  {text}
                </Box>
              </Typography>
            </ListItemText>
          </ListItemIcon>
        </ListItem>
      );
    });
  const handleListItemClick = (e, index) => {
    setSelectedIndex(index);
  };

  return (
    <div className={classes.root}>
      <ListData />
    </div>
  );
};

export default DrawerList;

DrawerListStyle.js just an stylejs file and can be ignored

import { makeStyles } from "@material-ui/core";

const useStyles = makeStyles((theme) => ({
  root: {
    marginTop: theme.spacing(2)
  },
  iconStyle: {
    margin: theme.spacing(0, 0, 1, 0),
    color: "#6A2CD8"
  },
  iconTitle: {
    margin: theme.spacing(0, 0, 0, 1),
    color: "#555458"
  }
}));

export { useStyles };

Material-UI sets the color of your ListItemIcon when the ListItem is selected, but because your custom svg icons already have the fill attribute set to another color, it overrides the color from MUI. The fix is simple, override the fill attribute again in your custom svg using makeStyles :

const useStyles = makeStyles((theme) => ({
  {...}
  listItem: {
    "&.Mui-selected": {
      "& path": {
        fill: "white"
      }
    }
  }
}));
<ListItem className={classes.listItem}

Live Demo

编辑 67092230/why-material-ui-classes-isnt-working-with-external-svg-icon

you can also use SVGR to build your own icons.

you can follow these steps:

1- install svgr using

npm install --save-dev @svgr/cli
# or use yarn
yarn add --dev @svgr/cli

2- add your icons in public/icons

3- create a file named svgr.config.js and place it in the root of project

module.exports = {
    icon: true,
    dimensions: false,
    expandProps: false,
    replaceAttrValues: {
        '#000': 'currentColor',
        '#292D32': 'currentColor',
        '#292d32': 'currentColor',
        '#55BB9D': 'currentColor',
        '#FFBC50': 'currentColor',
        '#A7A7A7': 'currentColor'
    }
};

4- add script in package.json "svg": "svgr --ext=jsx -d src/@share/icons public/icons"

5 - run yarn svg .

once you run above command svgr grab all icons in the public folder and gerenate React component inside src/@share/icons folder based on the config you provided. and replace every color text of svg into current color. in this case replace every key in replaceAttrValues to the currentColor .

then in your react component you can simply do this:

import { MyIcon } from '@share/icons';
import { SvgIcon } from '@mui/material';

function MyComponent() {
    return <p><SvgIcon component={MyIcon} /> check this icon </p>
};

export default MyComponent;

in this way you can change the color the way you change the text color of p element.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM