philcruz philcruz - 9 days ago 5
React JSX Question

How to make a Drawer like in Google Inbox with Material-UI

I'm building an app with Meteor, React and Material-UI. I have a custom left nav component based on Drawer.

class LeftNav extends Component {

constructor(props){
super(props);
}

render() {

return (
<Drawer open={this.props.open}
docked={false}
onRequestChange={this.props.handleOnRequestChange}
>
<Card >
<CardMedia overlay={<div><Avatar src='avatar.png' size={50} style={styles.avatar} /> <CardTitle title="Phil Cruz" subtitle="phil@philcruz.com" titleColor={darkWhite} subtitleColor={lightWhite}/></div>} >
<img src="/left_nav_wallpaper.jpg" />
</CardMedia>
</Card>
<MenuItem primaryText="testdomain1.com" leftIcon={<ActionGrade />} />
<MenuItem primaryText="testdomain2.com" leftIcon={<ActionHttp />}/>
<MenuItem primaryText="Add site..." leftIcon={<ContentAdd />} />
<Divider />
<MenuItem primaryText="Settings" leftIcon={<Settings />} />
<MenuItem primaryText="Help & About" leftIcon={<HelpOutline />} />
</Drawer>
);
}
};


I want to make it so it behaves like the Google InBox left nav/drawer. It has 3 sections: The top with the cover image and avatar, middle section and the bottom section.

enter image description here

I want the same behavior where:


  • the bottom menu items are fixed to the bottom (in red in the below
    image)

  • the top section can scroll



How can I do that?

Answer

By reading these questions/answers:

How to create a sticky footer inside the LeftNav?

Get viewport/window height in ReactJS

Reactjs - Rerender on browser resize

I was able to come up with a solution. Basically, you need to put the content in 2 divs. Use absolute positioning in the bottom div to make it fixed to the bottom. You then need to calculate the height of the top div based on the height of the window minus the height of the footer div. Listen to the resize window event so you can update the div height manually as the browser is resized.

Here's the code:

class LeftNav extends Component {

    constructor(props){
        super(props);
        this.state = { };
        this.updateDimensions = this.updateDimensions.bind(this);
    }

    componentDidMount(){
            this.updateDimensions();
            window.addEventListener("resize", this.updateDimensions);
    }

    updateDimensions() {
        this.setState({
            height: window.innerHeight,
            footerHeight: document.getElementById('leftNavFooter').clientHeight
         });
    }

    render() {

        return (
            <Drawer open={this.props.open}
                            docked={false}
                            onRequestChange={this.props.handleOnRequestChange}
            >
            <div style={{overflowY: 'auto', overflowX: 'hidden', height: (this.state.height - this.state.footerHeight) + 'px'}}>
                <Card >
                    <CardMedia overlay={<div><Avatar src='avatar.png' size={50}  style={styles.avatar} /> <CardTitle title="Phil Cruz" subtitle="phil@philcruz.com" titleColor={darkWhite} subtitleColor={lightWhite}/></div>} >
                        <img src="/left_nav_wallpaper.jpg" />
                    </CardMedia>
                </Card>
                <Menu>
                    <MenuItem primaryText="testdomain1.com" leftIcon={<ActionGrade />}  />
                    <MenuItem primaryText="testdomain2.com" leftIcon={<ActionHttp />}/>
                    <MenuItem primaryText="Add site..." leftIcon={<ContentAdd />} />
                </Menu>
            </div>
            <div id="leftNavFooter" style={{position: 'absolute', bottom: 0, width: '100%', overflow: 'hidden'}}>
                <Divider />
                <Menu>
                    <MenuItem primaryText="Settings" leftIcon={<Settings />} />
                    <MenuItem primaryText="Help & About" leftIcon={<HelpOutline />}  />
                </Menu>
            </div>
            </Drawer>
        );
    }
};