src/components/fragments/Drawer/drawer2.jsx

import React, {useState, useEffect, useRef} from "react";
import PropTypes from "prop-types";
import Styled from "styled-components";
import cx from "classnames";
import {Portal, Condition} from "components/elements";
import {useWindowSize} from "hooks";
import {helpers} from "utils";
import css from "./drawer.module.scss";

// TODO: Refactor condition to be single export
const If = Condition.Condition;

const para = document.createElement("div");

para.style.height = "100vh";
para.style.width = "100%";
para.style.overflow = "hidden";
para.style.top = 0;
para.style.left = 0;
para.style.position = "absolute";

/**
 * Depending on the step, returns an appropriate widget
 *
 * @param {object} props
 * @param {JSX.Element} props.children - The contents of the drawer.
 * @param {string} props.className - first css class that that is applied to the portal that the drawer gets rendered in,
 * @param {string} props.containerClassName - Last CSS class to apply to the the portal
 * @param {string} props.contentClassName - CSS to apply to the Wrapper div on the drawer children
 * @param {boolean} props.destroyOnClose - Destroy the content of the drawer when its not visible,
 * @param {boolean} props.isOpen - control the open state of the drawer,
 * @param {boolean} props.maskable - render the page mask when its open,
 * @param {boolean} props.maskClosable - Toggle whether clicking on the mask closes the drawer
 * @param {Function} props.onChange - Callback to run when the open state of the drawer changes
 * @param {string} props.placement - Enum to render the draw on the right or left of the screen
 * @param {Function | JSX.Element} props.trigger - The react element or a function that is invoked with trigger class breakpoint and that will have onclick bound to it to toggle the drawer open state
 * @param props.breakpoints
 */
const Drawer = Styled((props) => {
  const {
    children,
    className,
    containerClassName,
    contentClassName,
    isOpen,
    maskable,
    maskClosable,
    onChange,
    placement,
    trigger,
    drawerProps,
  } = props;
  const ref = useRef();
  const {width} = useWindowSize({debounce: 200});
  const {name: breakPoint} = helpers.determineBreakPoint(width);
  const [isDrawerOpen, setIsDrawerOpen] = useState(isOpen || false);
  const toggle = () => setIsDrawerOpen(!isDrawerOpen);
  const close = () => {
    setIsDrawerOpen(false);
    if (drawerProps.onClose) {
      drawerProps.onClose();
    }
  };
  const containerClass = cx(
    className,
    css.drawer,
    isDrawerOpen ? css.active : "",
    placement === "right" ? css.right : css.left,
    containerClassName,
  );
  const contentClass = cx(css["content-wrapper"], contentClassName);

  useEffect(() => {
    setIsDrawerOpen(isOpen);
  }, [isOpen]);
  useEffect(() => {
    if (isDrawerOpen !== isOpen && onChange) {
      onChange(isDrawerOpen);
    }
    if (!isDrawerOpen) {
      const scrollY = document.body.style.top;
      document.body.style.overflow = "";
      document.body.style.touchAction = "";
      document.body.style.position = "";
      document.body.style.top = "";
      document.body.style.width = "";
      // eslint-disable-next-line
      window.scrollTo(0, parseInt(scrollY || "0") * -1);
    }
    if (isDrawerOpen) {
      const currentTop = window.scrollY;
      const header = document.querySelector("#header");
      if (header) {
        header.style.top = 0;
      }
      document.body.style.overflow = "hidden";
      document.body.style.touchAction = "none";
      document.body.style.position = "fixed";
      document.body.style.width = "100vw";
      document.body.style.top = `-${currentTop}px`;
    }
  }, [isDrawerOpen]);

  const render =
    typeof children === "function"
      ? React.Children.only(children({close}))
      : children;
  let renderTrigger = null;
  if (trigger) {
    if (typeof trigger === "function") {
      renderTrigger = trigger({breakPoint, triggerClass: css.trigger});
    } else {
      renderTrigger = trigger;
    }
  }

  return (
    <>
      {renderTrigger && React.cloneElement(renderTrigger, {onClick: toggle})}
      <Portal className={containerClass}>
        <If is={maskable}>
          <div
            className={css["drawer-mask"]}
            onClick={maskClosable ? close : null}
          />
        </If>
        <div
          ref={ref}
          className={contentClass}
          style={{
            transform: isDrawerOpen ? "none" : "translate3d(100%, 0, 0)",
          }}
        >
          <If is={isDrawerOpen}>{render}</If>
        </div>
      </Portal>
    </>
  );
})`
    ${({breakpoints}) =>
      `.${css["content-wrapper"]} { width: ${breakpoints.xs}}`}

    @media screen and (min-width: 48em) {
        ${({breakpoints}) =>
          `.${css["content-wrapper"]} { width: ${breakpoints.sm}}`}
    }

    @media screen and (min-width: 60em) {
        ${({breakpoints}) =>
          `.${css["content-wrapper"]} { width: ${breakpoints.md}}`}
    }

    @media screen and (min-width: 72em) {
        ${({breakpoints}) =>
          `.${css["content-wrapper"]} { width: ${breakpoints.lg}}`}
    }

    @media screen and (min-width: 84em) {
        ${({breakpoints}) =>
          `.${css["content-wrapper"]} { width: ${breakpoints.xl}}`}
    }

    @media screen and (min-width: 96em) {
        ${({breakpoints}) =>
          `.${css["content-wrapper"]} { width: ${breakpoints.xxl}}`}
    }
`;

Drawer.propTypes = {
  breakpoints: PropTypes.shape({
    lg: PropTypes.string,
    md: PropTypes.string,
    sm: PropTypes.string,
    xl: PropTypes.string,
    xs: PropTypes.string,
    xxl: PropTypes.string,
  }),
  className: PropTypes.string,
  containerClassName: PropTypes.string,
  contentClassName: PropTypes.string,
  destroyOnClose: PropTypes.bool,
  isOpen: PropTypes.bool,
  maskable: PropTypes.bool,
  maskClosable: PropTypes.bool,
  placement: PropTypes.oneOf(["top", "right", "left", "right"]),
};
Drawer.defaultProps = {
  breakpoints: {
    lg: "50vw",
    md: "50vw",
    sm: "80vw",
    xl: "50vw",
    xs: "100vw",
    xxl: "40vw",
  },
  className: "",
  containerClassName: "",
  contentClassName: "",
  destroyOnClose: true,
  isOpen: false,
  maskable: true,
  maskClosable: true,
  placement: "right",
};

Drawer.displayName = "Drawer";

export {Drawer};
export default Drawer;