Source

Utils/Dialogs/FullscreenDialog/FullscreenHeader.js

import React, { useEffect } from "react";
import PropTypes from "prop-types";
import clsx from "clsx";
import { makeStyles } from "@material-ui/core/styles";
import CustomHeader from "../../Surfaces/CustomHeader";
import Typography from "@material-ui/core/Typography";
import CustomTooltip from "../../DataDisplay/CustomTooltip";
import { StyledIconButton } from "../../Buttons";
import WindowClose from "mdi-material-ui/WindowClose";

const useStyles = makeStyles(theme=> ({
    root: {
        padding: "16px 0 16px 24px"
    },
    rootOverflow: {
        overflowX: "hidden",
        overflowY: "hidden",
        scrollbarWidth: "thin",
        '&:hover': {
            overflowX: "auto",
        },
        '&::-webkit-scrollbar': {
            height: 8,
        },
        '&::-webkit-scrollbar-thumb': {
            backgroundColor: theme.palette.background.sub,
            borderRadius: 4,
            '&:hover': {
                backgroundColor: theme.palette.background.subDark,
            }
        },
        '&::-webkit-scrollbar-track': {
            boxShadow: "inset 0 0 6px rgba(0,0,0,0.3)",
            borderRadius: 4
        }
    },
    title: {
        fontSize: 20,
        fontWeight: 700,
        marginRight: "auto",
        paddingRight: 8,
    },
    titleFlex: {
        alignItems: "center",
        display: "flex",
        minWidth: "fit-content",
        whiteSpace: "nowrap",
        '& > div ~ div': {
            marginLeft: 8,
        },
    },
    close: {
        alignItems: "center",
        display: "flex",
        padding: "0 24px 0 16px",
    },
    closeSticky: {
        backgroundColor: theme.palette.background.main1,
        boxShadow: `-8px 0 6px -6px ${theme.palette.background.main1}`,
        position: "sticky",
        right: 0,
    },
    optional: {
        fontSize: "smaller",
        minWidth: "fit-content",
        marginRight: 16
    },
    optionalFlex: {
        display: "flex",
        flexDirection: "column",
        whiteSpace: "pre",
        '& > *': {
            lineHeight: 1,
            textAlign: "right"
        },
        '& > *:not(:first-child)': {
            marginTop: "1em",
        }
    }
}), {name: "FullscreenHeader"});

function HeaderElement(props) {
    const { children, component, ...other } = props;
    return React.createElement(component, { ...other }, children);
}

HeaderElement.propTypes = {
    component: PropTypes.elementType.isRequired
};

/**
 * <h3>Overview</h3>
 * Vertically scrollable header with default close button.
 * You are able to define your own root node as well as icon and tooltip for <code>CloseButton</code>.
 *
 * <h3>Usage</h3>
 * This component should be used with a <code>{@link FullscreenDialog}</code> as a root node.
 *
 * @constructor
 * @category Utils
 * @subcategory Dialogs
 * @param {Object} props
 * @param {React.ReactNode} [props.closeIcon] - An element placed inside of the <code>CloseButton</code> element.
 * @param {Object} [props.CloseButtonProps] - Props applied to the <code>CloseButton</code> element.
 * @param {Object} [props.CloseTooltipProps] - Props applied to the <code>CloseButton</code> tooltip.
 * @param {React.ElementType} [props.HeaderComponent=HeaderElement] - The component used for the root node.
 * @param {Object}[props.HeaderProps] - Props applied to the root node.
 * @param {string} props.id - The id of the component.
 * @param {function} [props.onClose] - Callback fired when the component requests to be closed.
 * @param {React.ReactNode} [props.optional] - An element placed before <code>CloseButton</code>.
 * @param {React.ReactNode} [props.title] - The content of the component.
 * @returns {React.ReactElement}
 */
function FullscreenHeader(props) {
    const {
        closeIcon,
        CloseButtonProps,
        CloseTooltipProps,
        HeaderComponent,
        HeaderProps,
        id,
        onClose,
        optional,
        title
    } = props;

    const classes = useStyles();
    const titleIsArray = React.isValidElement(title);
    const optionalIsArray = React.isValidElement(optional);

    const onMouseEnter = () => {
        document.getElementById(id).style.overflowX = "hidden";
    }

    const onMouseLeave = () => {
        document.getElementById(id).style.overflowX = "";
    }

    const onWheel = (event) => {
        let bar = document.getElementById(id);
        let barScrollPosition = document.getElementById(id).scrollLeft;

        bar.scrollTo({
            behavior: "auto",
            left: barScrollPosition + event.deltaY,
            top: 0,
        });
    };

    useEffect(() => {
        document.addEventListener("mousewheel", onWheel, {passive: false});

        return () => {
            document.removeEventListener("mousewheel", onWheel);
        }
    });

    return (
        <HeaderElement
            className={clsx(classes.root, classes.rootOverflow)}
            component={HeaderComponent}
            id={id}
            onWheel={onWheel}
            {...HeaderProps}
        >
            <div className={clsx(classes.title, {[classes.titleFlex]: titleIsArray})}>
                {title}
            </div>
            <div className={clsx(classes.close, classes.closeSticky)}>
                {optional &&
                    <Typography
                        className={clsx(classes.optional, {[classes.optionalFlex]: optionalIsArray})}
                        component={"div"}
                        variant={"button"}
                    >
                        {optional}
                    </Typography>
                }
                <CustomTooltip title={"Close details"} {...CloseTooltipProps}>
                    <StyledIconButton
                        aria-label={"close-button"}
                        color={"secondary"}
                        onClick={onClose}
                        onMouseEnter={onMouseEnter}
                        onMouseLeave={onMouseLeave}
                        {...CloseButtonProps}
                    >
                        {closeIcon != null ? closeIcon : <WindowClose />}
                    </StyledIconButton>
                </CustomTooltip>
            </div>
        </HeaderElement>
    )
}

FullscreenHeader.propTypes = {
    closeIcon: PropTypes.node,
    CloseButtonProps: PropTypes.object,
    CloseTooltipProps: PropTypes.object,
    HeaderComponent: PropTypes.elementType,
    HeaderProps: PropTypes.object,
    id: PropTypes.string.isRequired,
    onClose: PropTypes.func,
    optional: PropTypes.node,
    title: PropTypes.node
};

FullscreenHeader.defaultProps = {
    HeaderComponent: CustomHeader
};

export default FullscreenHeader;