3

Here is a createStyles statement in my Typescript code:

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    menuOpen: {
      zIndex: 1,
      width: 200,
      height: '100%',
      position: 'fixed',
      top: 48,
      transition: 'left .1s',
      marginRight: theme.spacing(2),
      left: 0,
      background: '#3f51b5',
      '& div:first-element': {
        marginTop: 100
      }
    },
    menuClose: {
      zIndex: 1,
      width: 200,
      height: '100%',
      position: 'fixed',
      top: 48,
      transition: 'left .1s',
      marginRight: theme.spacing(2),
      left: -200,
      background: '#3f51b5',
      '& div:first-element': {
        marginTop: 100
      }
    },
  }),
);

I'm flipping between these to open or close a menu (using a useState variable). As you can see, these styles share common attributes. How would I be able to make a menu style that these two styles could inherit from?

Edit: So it looks like the transitioning stuff isn't working and I'm wondering if thats because React reload those DOM elements? I tried adding the transitions in the menuClose and menuOpen styles just in case it needed them but the menu acts like it doesnt have a transition attribute.

After @doglozano's answer I have the following in my createStyles function:

// I've even tried putting the transition in a div wrapper
// but I think that element is getting overwritten also.
toolBar: {
  '& div': {
    transition: 'left .1s'
  }
},
menu: {
  zIndex: 1,
  width: 200,
  height: '100%',
  position: 'fixed',
  top: 48,
  transition: 'left .1s',
  marginRight: theme.spacing(2),
  left: -200,
  background: '#3f51b5',
  '& div:first-element': {
    marginTop: 100
  }
},
menuOpen: {
  left: 0,
  transition: 'left .1s',
},
menuClose: {
  left: -200,
  transition: 'left .1s',
},

When my menu is closed:

.makeStyles-menuClose-7 {
    left: -200px;
    transition: left .1s;
}
.makeStyles-menu-5 {
    top: 48px;
    left: -200px;
    width: 200px;
    height: 100%;
    z-index: 1;
    position: fixed;
    background: #3f51b5;
    transition: left .1s;
    margin-right: 16px;
}
.makeStyles-toolBar-4 {
    transition: left .1s;
}

When my menu is open:

.makeStyles-menuOpen-6 {
    left: 0;
    transition: left .1s;
}
.makeStyles-menu-5 {
    top: 48px;
    left: -200px;
    width: 200px;
    height: 100%;
    z-index: 1;
    position: fixed;
    background: #3f51b5;
    transition: left .1s;
    margin-right: 16px;
}
.makeStyles-toolBar-4 {
    transition: left .1s;
}

Edit 2: Here is my Sandbox link.

Any ideas?

aarona
  • 35,986
  • 41
  • 138
  • 186

1 Answers1

3

You could define a base class with all the common attributes, and then have only the specific attributes in menuOpen and menuClose. Then, you can use a library such as classnames or clsx to apply the base class always, and then conditionally apply also either menuOpen or menuClose based on your state variable.

Would look something like this:


import clsx from 'clsx';

...

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    menu: {
      zIndex: 1,
      width: 200,
      height: '100%',
      position: 'fixed',
      top: 48,
      transition: 'left .1s',
      marginRight: theme.spacing(2),
      background: '#3f51b5',
      '& div:first-element': {
        marginTop: 100
      }
    }
    menuOpen: {
      left: 0,
    },
    menuClose: {
      left: -200,
    },
  }),
);

...

const YourStyledCommponent = ({ classes }) => {

   const [isOpen, setOpen] = useState(false);

   return (
      <SomeMuiComponent 
         className={clsx(classes.menu, {
            [classes.menuOpen]: isOpen,
            [classes.menuClose]: !isOpen,
         })}
      />
   );
}

Of course you could also say that your base menu style will be the close state, and then you end up having only one additional style:


import clsx from "clsx";

...

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    menu: {
      zIndex: 1,
      width: 200,
      height: '100%',
      position: 'fixed',
      top: 48,
      transition: 'left .1s',
      marginRight: theme.spacing(2),
      background: '#3f51b5',
      left: -200,
      '& div:first-element': {
        marginTop: 100
      }
    }
    menuOpen: {
      left: 0,
    },
  }),
);

...

const YourStyledCommponent = ({ classes }) => {

   const [isOpen, setOpen] = useState(false);

   return (
      <SomeMuiComponent 
         className={clsx(classes.menu, {
            [classes.menuOpen]: isOpen,
         })}
      />
   );
}

EDIT: as stated by @Ryan Cogswell in the comments, clsx would be the recommended library if you are using Material UI >= v4.0, since it already comes included in it .

dglozano
  • 6,369
  • 2
  • 19
  • 38
  • 1
    I was about to post something fairly similar. One little recommendation would be to use `clsx` rather than `classnames` since `clsx` is already included within Material-UI. Material-UI switched its internal usages from `classnames` to `clsx` with v4. – Ryan Cogswell Feb 09 '21 at 16:27
  • @RyanCogswell nice input did not know that. Have edited my answer to include your suggestion – dglozano Feb 09 '21 at 16:34
  • @dglozano and @RyanCogswell, Thanks for the help, guys. I've applied your answer and its doing mostly well except for the `transition` attribute. It seems to be failing. I've updated the question if you guys could take a look. If you need more code, let me know. – aarona Feb 10 '21 at 01:14
  • @aarona Please create a code sandbox reproducing your problem with the transition. Here's a sandbox I created (showing two different possible approaches) when I was about to answer your question (but dglozano beat me to it): https://codesandbox.io/s/common-styles-zc3po?file=/demo.tsx – Ryan Cogswell Feb 10 '21 at 01:46
  • @aarona could you post your code, including JSX showing to which MUI component you are trying to apply the style to? What might be happening is that the there is a MUI style with more specificity that's overriding your transition. For example, if you are using the `Menu` component, it internally uses a `Popover` component with a `Transition` component, which is most likely taking precedence over your styling... but that's just a wild guess – dglozano Feb 10 '21 at 06:58
  • dglozano and @RyanCogswell see 2nd edit. Posted a CodeSandbox link. – aarona Feb 10 '21 at 17:28
  • 2
    @aarona The transition problem was due to nesting the `UserMenu` component within another component. Here's a fixed version: https://codesandbox.io/s/determined-leftpad-i8i0c?file=/src/Header.tsx. You can find some explanation in my related answers (https://stackoverflow.com/questions/55192935/react-material-ui-menu-anchor-broken-by-react-window-list/55203218#55203218, https://stackoverflow.com/questions/57433546/react-slider-with-custom-hook-not-working-properly/57434343#57434343). – Ryan Cogswell Feb 10 '21 at 18:03
  • @RyanCogswell if you'd like, you can submit an answer and I'll mark it as the correct one or if you'd like to defer to dglozano, he can edit his post and I'll mark that as correct. I already +1'ed your other answer Ryan as well as this one dglozano. – aarona Feb 10 '21 at 18:42
  • @RyanCogswell feel free to post your answer and get it marked as correct, I don't mind – dglozano Feb 10 '21 at 18:44
  • 1
    @aarona That's OK. dglozano sufficiently answered the original question. The transition issue was an independent problem that couldn't be addressed without seeing further code that was present in the sandbox. – Ryan Cogswell Feb 10 '21 at 18:46
  • Yes, that's very true! Thank you for the additional help. – aarona Feb 10 '21 at 19:02
  • @RyanCogswell fyi: https://stackoverflow.com/questions/66143491/how-to-make-a-custom-menu-close-when-i-click-somewhere-else-other-than-the-menu – aarona Feb 10 '21 at 19:12