DocSpace-client/packages/asc-web-components/modal-dialog/index.js

300 lines
8.1 KiB
JavaScript

import React from "react";
import PropTypes from "prop-types";
import Backdrop from "../backdrop";
import Aside from "../aside";
import Heading from "../heading";
import { getModalType } from "../utils/device";
import throttle from "lodash/throttle";
import Box from "../box";
import {
CloseButton,
StyledHeader,
Content,
Dialog,
BodyBox,
} from "./styled-modal-dialog";
import Portal from "../portal";
import Loaders from "@appserver/common/components/Loaders";
function Header() {
return null;
}
Header.displayName = "DialogHeader";
function Body() {
return null;
}
Body.displayName = "DialogBody";
function Footer() {
return null;
}
Footer.displayName = "DialogFooter";
class ModalDialog extends React.Component {
static Header = Header;
static Body = Body;
constructor(props) {
super(props);
this.state = { displayType: this.getTypeByWidth() };
this.getTypeByWidth = this.getTypeByWidth.bind(this);
this.resize = this.resize.bind(this);
this.popstate = this.popstate.bind(this);
this.throttledResize = throttle(this.resize, 300);
}
getTypeByWidth() {
if (this.props.displayType !== "auto") return this.props.displayType;
return getModalType();
}
resize() {
if (this.props.displayType !== "auto") return;
const type = this.getTypeByWidth();
if (type === this.state.displayType) return;
this.setState({ displayType: type });
this.props.onResize && this.props.onResize(type);
}
popstate() {
window.removeEventListener("popstate", this.popstate, false);
this.props.onClose();
window.history.go(1);
}
componentDidUpdate(prevProps) {
if (this.props.displayType !== prevProps.displayType) {
this.setState({ displayType: this.getTypeByWidth() });
}
if (this.props.visible && this.state.displayType === "aside") {
window.addEventListener("popstate", this.popstate, false);
}
}
componentDidMount() {
window.addEventListener("resize", this.throttledResize);
window.addEventListener("keyup", this.onKeyPress);
window.onpopstate = () => {
this.props.onClose();
};
}
componentWillUnmount() {
window.removeEventListener("resize", this.throttledResize);
window.removeEventListener("keyup", this.onKeyPress);
}
onKeyPress = (event) => {
if (event.key === "Esc" || event.key === "Escape") {
this.props.onClose();
}
};
render() {
const {
visible,
scale,
onClose,
zIndex,
asideBodyPadding,
modalBodyPadding,
contentHeight,
contentWidth,
className,
id,
style,
children,
isLoading,
contentPaddingBottom,
withoutBodyScroll,
modalLoaderBodyHeight,
withoutCloseButton,
theme,
width,
} = this.props;
let header = null;
let body = null;
let footer = null;
React.Children.forEach(children, (child) => {
const childType =
child && child.type && (child.type.displayName || child.type.name);
switch (childType) {
case Header.displayName:
header = child;
break;
case Body.displayName:
body = child;
break;
case Footer.displayName:
footer = child;
break;
default:
break;
}
});
const renderModal = () => {
return this.state.displayType === "modal" ? (
<Backdrop
visible={visible}
zIndex={zIndex}
withBackground={true}
isModalDialog
>
<Dialog
width={width}
className={`${className} not-selectable`}
id={id}
style={style}
>
<Content
contentHeight={contentHeight}
contentWidth={contentWidth}
displayType={this.state.displayType}
>
{isLoading ? (
<Loaders.DialogLoader bodyHeight={modalLoaderBodyHeight} />
) : (
<>
{header && (
<StyledHeader>
<Heading
className="heading"
size="medium"
truncate={true}
>
{header ? header.props.children : null}
</Heading>
{!withoutCloseButton && (
<CloseButton
className="modal-dialog-button_close"
onClick={onClose}
></CloseButton>
)}
</StyledHeader>
)}
<BodyBox paddingProp={modalBodyPadding}>
{body ? body.props.children : null}
</BodyBox>
<Box className="modal-dialog-modal-footer">
{footer ? footer.props.children : null}
</Box>
</>
)}
</Content>
</Dialog>
</Backdrop>
) : (
<Box className={className} id={id} style={style}>
<Backdrop
visible={visible}
onClick={onClose}
zIndex={zIndex}
isAside={true}
/>
<Aside
visible={visible}
scale={scale}
zIndex={zIndex}
onClose={onClose}
contentPaddingBottom={contentPaddingBottom}
className="modal-dialog-aside not-selectable"
withoutBodyScroll={withoutBodyScroll}
>
<Content
contentHeight={contentHeight}
contentWidth={contentWidth}
withoutBodyScroll={withoutBodyScroll}
displayType={this.state.displayType}
>
{isLoading ? (
<Loaders.DialogAsideLoader withoutAside />
) : (
<>
<StyledHeader className="modal-dialog-aside-header">
<Heading className="heading" size="medium" truncate={true}>
{header ? header.props.children : null}
</Heading>
{scale ? <CloseButton onClick={onClose}></CloseButton> : ""}
</StyledHeader>
<BodyBox
className="modal-dialog-aside-body"
paddingProp={asideBodyPadding}
withoutBodyScroll={withoutBodyScroll}
>
{body ? body.props.children : null}
</BodyBox>
<Box className="modal-dialog-aside-footer">
{footer ? footer.props.children : null}
</Box>
</>
)}
</Content>
</Aside>
</Box>
);
};
const modalDialog = renderModal();
return <Portal element={modalDialog} />;
}
}
ModalDialog.propTypes = {
children: PropTypes.any,
/** Display dialog or not */
visible: PropTypes.bool,
/** Display type */
displayType: PropTypes.oneOf(["auto", "modal", "aside"]),
/** Indicates the side panel has scale */
scale: PropTypes.bool,
/** Will be triggered when a close button is clicked */
onClose: PropTypes.func,
onResize: PropTypes.func,
/**Display close button or not */
withoutCloseButton: PropTypes.bool,
/** CSS z-index */
zIndex: PropTypes.number,
/** CSS padding props for body section */
asideBodyPadding: PropTypes.string,
modalBodyPadding: PropTypes.string,
contentHeight: PropTypes.string,
contentWidth: PropTypes.string,
isLoading: PropTypes.bool,
withoutBodyScroll: PropTypes.bool,
className: PropTypes.string,
id: PropTypes.string,
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
contentPaddingBottom: PropTypes.string,
modalLoaderBodyHeight: PropTypes.string,
width: PropTypes.string,
};
ModalDialog.defaultProps = {
displayType: "auto",
zIndex: 310,
asideBodyPadding: "16px 0",
modalBodyPadding: "12px 0",
contentWidth: "100%",
withoutCloseButton: false,
withoutBodyScroll: false,
};
ModalDialog.Header = Header;
ModalDialog.Body = Body;
ModalDialog.Footer = Footer;
export default ModalDialog;