tony_k tony_k - 27 days ago 16
React JSX Question

material-ui: AppBar: strategy for restricting an image height to AppBar height?

can anyone provide guidance around an idiomatic way to place an image in an AppBar and have it be restricted to the standard material height (e.g. 64px for a desktop)?

i'm currently using

material-ui@next
(
1.0.0-beta.2
currently).

i have found that something like:

<AppBar>
<Toolbar>
<IconButton color="contrast" aria-label="Menu">
<MenuIcon />
</IconButton>
<img src={logo} style={{height: 64}}/>
<Typography type="title" color="inherit">
React Scratch
</Typography>
</Toolbar>
</AppBar>


works well.

the actual logo is a png file with a height greater than 64, so if i don't ratchet it down, it expands the height of the AppBar out of Material spec.

in the current master branch version of
src/styles
there is a
getMuiTheme.js
which seems to deliver this height readily, but in the
@next
version i am looking at, that file doesn't even exist and tbh, i can't easily determine how that height is being set anymore.

i found that the AppBar is currently being renovated for composability, so that churn might make it challenging to answer this question, but just in case anyone has a good handle on this, i figured i would toss the question out there.

thanks!

Answer Source

In all cases I've seen, an AppBar is implemented with a Toolbar as it's first child. The Toolbar's stylesheet dictates it's height based on the breakpoints defined in the theme.

Take a look here: https://github.com/callemall/material-ui/blob/v1-beta/src/Toolbar/Toolbar.js

You can use a similar approach to define a stylesheet with a class for your AppBar images that varies the height for the applicable breakpoints. Then when rendering the component, apply the class to your image.

Note: if you use the withStyles HOC, as is done in the Toolbar, AppBar etc, the classes defined in that stylesheet will be available through a prop named classes.

You are right about the AppBar's need for composability, but that issue has not been solved yet, and this is the beta branch anyway. When it is solved, there should be a better solution that would be worth migrating towards.

I hope this answer helps. I would have added code samples but I am answering from my phone while waiting in a grocery store parking lot. If I get a chance I will update this answer.

Here's one approach, duplicating the styles in a new reusable component:

import createStyleSheet from 'material-ui/styles/createStyleSheet';
import withStyles from 'material-ui/styles/withStyles';

// define these styles once, if changes are needed because of a change
// to the material-ui beta branch, the impact is minimal
const styleSheet = createStyleSheet('ToolbarImage', theme => ({
    root: {
        height: 56,
        [`${theme.breakpoints.up('xs')} and (orientation: landscape)`]: {
            height: 48,
        },
        [theme.breakpoints.up('sm')]: {
            height: 64,
        },
    },
}));

// a reusable component for any image you'd need in a toolbar/appbar
const ToolbarImage = (props) => {
    const { src, classes } = this.props;
    return (
        <img src={src} className={classes.root} />
    );
};

// this higher order component uses styleSheet to add
// a classes prop that contains the name of the classes
export default withStyles(styleSheet)(ToolbarImage);

Another approach is to add the standard toolbar heights to the theme as business variables, override the root class for all Toolbars so that it makes use of them, and use the theme whenever you need to reference them again:

  // define the standard heights in one place
  const toolbarHeights = {
    mobilePortrait: 56,
    mobileLandscape: 48,
    tabletDesktop: 64,
  };

  // create the theme as you normally would, but add the heights
  let theme = createMuiTheme({
    palette: createPalette({
      primary: blue,
      accent: pink,
    }),
    standards: {
      toolbar: {
        heights: toolbarHeights,
      },
    },
  });

  // recreate the theme, overriding the toolbar's root class
  theme = createMuiTheme({
    ...theme,
    overrides: {
      MuiToolbar: {
        // Name of the styleSheet
        root: {
          position: 'relative',
          display: 'flex',
          alignItems: 'center',
          minHeight: theme.standards.toolbar.heights.mobilePortrait,
          [`${theme.breakpoints.up('xs')} and (orientation: landscape)`]: {
            minHeight: theme.standards.toolbar.heights.mobileLandscape,
          },
          [theme.breakpoints.up('sm')]: {
            minHeight: theme.standards.toolbar.heights.tabletDesktop,
          },
        },
      },
    },
  });

Then you can reference these heights in any stylesheet you create because they're part of the theme.

UPDATED FOLLOWING THE RELEASE OF 1.0.0-beta.11:

There is now a toolbar mixin available on the theme that provides the toolbar minHeight for each breakpoint. If you need to style an element relative to the standard height of the AppBar component, you can use this object to build your own styles:

const toolbarRelativeProperties = (property, modifier = value => value) => theme =>
    Object.keys(theme.mixins.toolbar).reduce((style, key) => {
        if (key === 'minHeight') {
            return { ...style, [property]: modifier(theme.mixins.toolbar[key]) };
        }
        return { ...style, [key]: { [property]: modifier(theme.mixins.toolbar[key].minHeight) } };
    }, {});

const styles = theme => ({
    elementBeneathFixedToolbarWithPadding: {
        ...toolbarRelativeProperties(
            'paddingTop',
            toolbarHeight => toolbarHeight + theme.spacing.unit * 2,
        )(theme),
    },
    elementFixedBeneathFixedToolbar: {
        ...toolbarRelativeProperties('top')(theme),
    },
});

In this example, toolbarRelativeProperties returns a function that will return an object that can be deconstructed into your style object. It addresses the simple case of setting a specified property to a value that is based on the AppBar height.