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;