Lasse
Lasse

Reputation: 601

Scrollable div in react component

I'm to build a calendar web application. For that I need a week view, showing all events on that week. My go to reference is Google Calendar because the design is modern and is composed really well. So for the application I'm using React with redux as state management. But I seem to have run in to a problem that has been bugging me for a while now. Getting the scroll to work as the scroll in Google calendar week view, (Vertical scroll that is).

The important components: Lowest level component (name: body)

import React from 'react';
import { withStyles, withWidth, Typography } from '@material-ui/core';
import moment from 'moment';
import {
    weekHeadMaxWidthXl,
    weekHeadMinWidthXl,
    weekHeadMaxWidthLg,
    weekHeadMinWidthLg,
    weekHeadMaxWidthMd,
    weekHeadMinWidthMd,
    weekHeadMaxWidthSm,
    weekHeadMinWidthSm,
    weekHeadMaxWidthXs,
    weekHeadMinWidthXs,
    weekHeadHightXl
} from '../../../util/dimension';
const style = theme => ({
    bodyContainer: {
        display: "flex",
        flexDirection: "row",
        overflow: "auto",
        width: "100%",
    },
    hourContainer: {
        display: "flex",
        flexDirection: "column",
        marginTop: 1,
        [theme.breakpoints.only('xl')]: {
            maxWidth: weekHeadMaxWidthXl,
            minWidth: weekHeadMinWidthXl,
            width: "100%",
        },
        [theme.breakpoints.only('lg')]: {
            maxWidth: weekHeadMaxWidthLg,
            minWidth: weekHeadMinWidthLg,
            width: "100%",
        },
        [theme.breakpoints.only('md')]: {
            maxWidth: weekHeadMaxWidthMd,
            minWidth: weekHeadMinWidthMd,
            width: "100%",
        },
        [theme.breakpoints.only('sm')]: {
            maxWidth: weekHeadMaxWidthSm,
            minWidth: weekHeadMinWidthSm,
            width: "100%",
            backgroundColor: "#"
        },
        [theme.breakpoints.only('xs')]: {
            maxWidth: weekHeadMaxWidthXs,
            minWidth: weekHeadMinWidthXs,
            width: "100%",
        },

    },
    hour: {
        [theme.breakpoints.only('xl')]: {
            maxHeight: "50px",
            height: "46px",
            minHeight: "44px",
        },
        [theme.breakpoints.only('lg')]: {
            maxHeight: "50px",
            height: "46px",
            minHeight: "44px",
        },
        [theme.breakpoints.only('md')]: {
            maxHeight: "50px",
            height: "46px",
            minHeight: "44px",
        },
        [theme.breakpoints.only('sm')]: {
            maxHeight: "50px",
            height: "46px",
            minHeight: "44px",
        },
        borderBottom: "solid 1px #e7e7e7",
        borderLeft: "solid 1px #e7e7e7",
        backgroundColor: "#FFF"
    },
    offset: {
        display: "flex",
        justifyContent: "center",
        paddingTop: 10,
        backgroundColor: "#FFF",
        borderBottom: "solid 1px #e7e7e7",
        borderTop: "solid 1px #e7e7e7",
        [theme.breakpoints.only('xl')]: {
            maxWidth: 55,
            minWidth: 50,
            width: 53,
        },
        [theme.breakpoints.only('lg')]: {
            maxWidth: 53,
            minWidth: 47,
            width: 50,
        },
        [theme.breakpoints.only('md')]: {
            maxWidth: 50,
            minWidth: 43,
            width: 47,
        },
        [theme.breakpoints.only('sm')]: {
            maxWidth: 47,
            minWidth: 38,
            width: 43,

        },
        [theme.breakpoints.only('xs')]: {
            maxWidth: 47,
            minWidth: 43,
            width: 38,
        },
    },
});

const body = props => {
    const { classes } = props;
    let days = [];
    const getHours = () => {
            let hours = [];
            for (let i = 0; i < 24; i++) {
                hours.push(<div key={"hours " + i} className={classes.hour}>
                    <Typography> {i} </Typography>
                </div>)
            }
            return hours
        }
    days.push(<div key="offset" className={classes.offset}></div>)
    for (let j = 0; j < 7; j++) {
        days.push(<div key={"days " + j} className={classes.hourContainer}>
        {getHours()}
        </div>)
    }

    return (<div className={classes.bodyContainer}>
        {days}
    </div>);
}

export default withWidth()(withStyles(style)(body));

Component implementing body. (name: Calendar)

import React from 'react';
import { withStyles, Typography, TableCell } from '@material-ui/core';
import withWidth, { isWidthUp } from '@material-ui/core/withWidth';
import { connect } from 'react-redux'
import { changeWindowSizeAction } from '../../redux/actions/settingsActions';
import Month from './views/month/month';
import MonthOffset from './views/month/offset';
import WeekHead from './views/week/head';
import WeekBody from './views/week/body';


const styles = theme => ({
    week: {
        height: "100vh",
    },
    weekBody: {
        height: "100vh",
        overflowY: "auto"
    }
});


class Calendar extends React.Component {
    getFullDayBookings = bookings => {
        return bookings.filter(B => B.allDay);
    }
    generateComponent = (variant, props) => {
        switch (variant) {
            case "month":
                return <Month onDayClick={props.onDayClick} onBookingClick={props.onBookingClick} dateNr={props.nr} day={props.day} bookings={this.formatBookings()} />
            case "week":
                return this.generateWeek()
            case "day":
                return null;
            case "agenda":
                return null;
            case "offset":
                return <MonthOffset dateNr={props.nr} variant="month" />
            default:
                return (
                    <TableCell className={props.classes.monthCell}>

                    </TableCell>
                );
        }

    }
    generateWeek = () => {
        return (<div className={this.props.classes.week}>
            <WeekHead bookings={this.formatBookings().filter(E => E.allDay)} />
            <div className={this.props.classes.weekBody}>
                <WeekBody />
            </div>
        </div>)
    }
    formatBookings = () => {
        if (this.props.bookings.length > 0) {
            return this.filterBookings(this.props.bookings, this.props.bookingFilter)
        } else {
            return [];
        }

    }
    filterBookings = (bookings, filter) => {
        if (bookings.length > 0) {
            let notToRender = Object.keys(filter).filter(F => {
                if (!filter[F]) {
                    return F;
                }
            });
            return bookings.filter(E => {
                if (notToRender.length > 0) {
                    if (notToRender.indexOf(E.room) === -1) {
                        return E
                    }
                } else {
                    return E;
                }
            });
        } else {
            return []
        }
    }
    render() {
        return (this.generateComponent(this.props.variant, this.props))
    }

}

const mapDispatchToProps = dispatch => {
    return {
        changeWindowSize: wSize => (dispatch(changeWindowSizeAction(wSize)))
    }
}
const mapStateToProps = state => {
    return {
        windowSize: state.settings.windowSize,
    }
}

export default withWidth()(withStyles(styles)(connect(mapStateToProps, mapDispatchToProps)(Calendar)));

Component implementing Calendar (name: week)

import React from 'react';
import { connect } from 'react-redux';
import Calendar from '../components/calendar/Calendar';
import { withStyles, Typography } from '@material-ui/core';
import moment from 'moment';


const styles = theme => ({

});


const testEvents = [
    { "title": "Test - NotAllDay", "id": 1, "room": "office" },
    { "title": "Test - NotAllDay", "id": 2, "room": "office" },
    { "title": "Test - Cafe", "id": 3, "room": "cafe", "allDay": true, "startDate": moment(), "endDate": moment().add(2, "days") },
    { "title": "Test - NotAllDay", "id": 11, "room": "office" }]



class Week extends React.Component {
    render(){
        return <Calendar variant="week" bookings={testEvents} bookingFilter={this.props.filter}/>
    }
}


const mapStateToPops = state => ({
    currentDate: state.settings.calendarState,
    filter: state.settings.roomsRender,
});

const mapDispatchToProps = dispatch => ({

});

export default withStyles(styles)(connect(mapStateToPops, mapDispatchToProps)(Week));

Last, Component implementing Week

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { withStyles } from '@material-ui/core/styles/';
import classNames from 'classnames';
import PropTypes from 'prop-types'; 
import Banner from '../containers/Banner';
import Sidebar from '../containers/Sidebar';
import Month from '../containers/Month';
import Week from '../containers/Week';
import './styles.css';
const styles = theme => ({
    DisplayContainer: {
        flex: 1,
        height: "100vh"
    },
    content: {
        height: "100%",
        flex: 3,
        flexGrow: 1,
        backgroundColor: theme.palette.background.default,
        transition: theme.transitions.create('margin', {
            easing: theme.transitions.easing.sharp,
            duration: theme.transitions.duration.leavingScreen,
        }),
    },
    'content-left': {
        marginLeft: 0,
    },
    contentShift: {
        transition: theme.transitions.create('margin', {
            easing: theme.transitions.easing.easeOut,
            duration: theme.transitions.duration.enteringScreen,
        }),
    },
    'contentShift-left': {
        marginLeft: 240,
    },
});


class Overview extends Component {
    calendarView = () => {
        switch (this.props.calendarType) {
            case "month":
                return <Month />
            case "week":
                return <Week />
            case "day":
                return <div></div>
            case "agenda":
                return <div></div>
            default:
                return <div></div>
        }
    }
    render() {
        const { classes, theme } = this.props;
        return (
            <div className={classes.DisplayContainer}>
                <div className="Banner">
                    <Banner />
                </div>
                <div>
                    <Sidebar />
                </div>
                <main className={classNames(classes.content, classes[`content-left`], {
                    [classes.contentShift]: this.props.showMenu,
                    [classes[`contentShift-left`]]: this.props.showMenu,
                })}>
                    {this.calendarView()}
                </main>
            </div>
        )
    }
}

Overview.propTypes ={
    classes: PropTypes.object.isRequired,
}

const mapStateToProps = state => {
    return {
        showMenu: state.settings.showMenu,
        calendarType: state.settings.calendarType,
    }
}


export default withStyles(styles)(connect(mapStateToProps)(Overview));

The Month view works as it should. Tho have some bugs, but the application is long from done. So, yeah if one could point me in the direction or provide some inside knowledge, that would be great!

Thanks in advice!

Upvotes: 1

Views: 8592

Answers (1)

Hriday Modi
Hriday Modi

Reputation: 2081

Here is the CSS fix:

Add this CSS to your application:

html, #root {
   height: 100%
}

.jss2.jss3{
   height: calc(100% - 65px);
}

.jss189 {
    height: calc(100% - 132px);
}

Upvotes: 1

Related Questions