Skip to content
Snippets Groups Projects
Unverified Commit d7a9c17d authored by Oisin Coveney's avatar Oisin Coveney Committed by GitHub
Browse files

Split Modal into separate TS files (#30845)

parent 7501b4b3
No related branches found
No related tags found
No related merge requests found
/* eslint-disable react/prop-types */
import React, { Component } from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import { CSSTransition, TransitionGroup } from "react-transition-group";
import { Motion, spring } from "react-motion";
import _ from "underscore";
import { getScrollX, getScrollY } from "metabase/lib/dom";
import SandboxedPortal from "metabase/components/SandboxedPortal";
import routeless from "metabase/hoc/Routeless";
import ModalContent from "metabase/components/ModalContent";
import {
BaseModalProps,
getModalContent,
} from "metabase/components/Modal/utils";
import { MaybeOnClickOutsideWrapper } from "metabase/components/Modal/MaybeOnClickOutsideWrapper";
function getModalContent(props) {
if (
React.Children.count(props.children) > 1 ||
props.title != null ||
props.footer != null
) {
return <ModalContent {..._.omit(props, "className", "style")} />;
} else {
return React.Children.only(props.children);
}
}
export class WindowModal extends Component {
static propTypes = {
isOpen: PropTypes.bool,
enableMouseEvents: PropTypes.bool,
enableTransition: PropTypes.bool,
closeOnClickOutside: PropTypes.bool,
};
static defaultProps = {
className: "Modal",
backdropClassName: "Modal-backdrop",
enableTransition: true,
};
constructor(props) {
super(props);
this._modalElement = document.createElement("div");
this._modalElement.className = "ModalContainer";
document.body.appendChild(this._modalElement);
}
componentWillUnmount() {
this._modalElement.parentNode.removeChild(this._modalElement);
}
handleDismissal = () => {
if (this.props.onClose) {
this.props.onClose();
}
};
_modalComponent() {
const className = cx(
this.props.className,
...["small", "medium", "wide", "tall", "fit"]
.filter(type => this.props[type])
.map(type => `Modal--${type}`),
);
return (
<MaybeOnClickOutsideWrapper
closeOnClickOutside={this.props.closeOnClickOutside}
backdropElement={this._modalElement}
handleDismissal={this.handleDismissal}
>
<div
className={cx(className, "relative bg-white rounded")}
role="dialog"
>
{getModalContent({
...this.props,
fullPageModal: false,
// if there is a form then its a form modal, or if there's a form
// modal prop use that
formModal: !!this.props.form || this.props.formModal,
})}
</div>
</MaybeOnClickOutsideWrapper>
);
}
export type FullPageModalProps = BaseModalProps & {
isOpen: boolean;
onClose?: () => void;
fullPageModal?: boolean;
};
render() {
const {
enableMouseEvents,
backdropClassName,
isOpen,
style,
enableTransition,
} = this.props;
const backdropClassnames =
"flex justify-center align-center fixed top left bottom right";
type FullPageModalState = {
isOpen: boolean;
};
return (
<SandboxedPortal
container={this._modalElement}
enableMouseEvents={enableMouseEvents}
>
<TransitionGroup
appear={enableTransition}
enter={enableTransition}
exit={enableTransition}
>
{isOpen && (
<CSSTransition
key="modal"
classNames="Modal"
timeout={{
appear: 250,
enter: 250,
exit: 250,
}}
>
<div
className={cx(backdropClassName, backdropClassnames)}
style={style}
>
{this._modalComponent()}
</div>
</CSSTransition>
)}
</TransitionGroup>
</SandboxedPortal>
);
}
}
export class FullPageModal extends Component<
FullPageModalProps,
FullPageModalState
> {
_modalElement: HTMLDivElement;
_scrollX: number;
_scrollY: number;
export class FullPageModal extends Component {
constructor(props) {
constructor(props: FullPageModalProps) {
super(props);
this.state = {
isOpen: true,
......@@ -168,7 +62,9 @@ export class FullPageModal extends Component {
}
componentWillUnmount() {
this._modalElement.parentNode.removeChild(this._modalElement);
if (this._modalElement.parentNode) {
this._modalElement.parentNode.removeChild(this._modalElement);
}
document.body.style.overflow = "";
}
......@@ -198,8 +94,8 @@ export class FullPageModal extends Component {
the OnClickOutsideWrapper popover stack. Otherwise, clicks within
this modal might be seen as clicks outside another popover. */}
<MaybeOnClickOutsideWrapper
closeOnClickOutside={this.props.closeOnClickOutside}
handleDismissal={this.handleDismissal}
closeOnClickOutside={this.props.closeOnClickOutside}
>
<div
className="full-height relative scroll-y"
......@@ -220,21 +116,3 @@ export class FullPageModal extends Component {
);
}
}
// the "routeless" version should only be used for non-inline modals
const RoutelessFullPageModal = routeless(FullPageModal);
const Modal = ({ full = false, ...props }) =>
full ? (
props.isOpen ? (
<RoutelessFullPageModal {...props} />
) : null
) : (
<WindowModal {...props} />
);
Modal.defaultProps = {
isOpen: true,
};
export default Modal;
import React from "react";
import { RoutelessFullPageModal } from "metabase/components/Modal/RoutelessFullPageModal";
import {
WindowModal,
WindowModalProps,
} from "metabase/components/Modal/WindowModal";
import type { FullPageModalProps } from "metabase/components/Modal/FullPageModal";
const Modal = ({
full = false,
...props
}: {
full?: boolean;
isOpen?: boolean;
} & (WindowModalProps & FullPageModalProps)) => {
if (full) {
return props.isOpen ? <RoutelessFullPageModal {...props} /> : null;
} else {
return <WindowModal {...props} />;
}
};
Modal.defaultProps = {
isOpen: true,
};
export { Modal };
// the "routeless" version should only be used for non-inline modals
import routeless from "metabase/hoc/Routeless";
import { FullPageModal } from "metabase/components/Modal/FullPageModal";
export const RoutelessFullPageModal = routeless(FullPageModal);
import React, { Component, CSSProperties } from "react";
import { CSSTransition, TransitionGroup } from "react-transition-group";
import cx from "classnames";
import {
getModalContent,
ModalSize,
modalSizes,
BaseModalProps,
} from "metabase/components/Modal/utils";
import SandboxedPortal from "metabase/components/SandboxedPortal";
import { MaybeOnClickOutsideWrapper } from "metabase/components/Modal/MaybeOnClickOutsideWrapper";
export type WindowModalProps = BaseModalProps & {
isOpen?: boolean;
onClose?: () => void;
fullPageModal?: boolean;
formModal?: boolean;
style?: CSSProperties;
} & {
[size in ModalSize]?: boolean;
};
export class WindowModal extends Component<WindowModalProps> {
_modalElement: HTMLDivElement;
static defaultProps = {
className: "Modal",
backdropClassName: "Modal-backdrop",
enableTransition: true,
};
constructor(props: WindowModalProps) {
super(props);
this._modalElement = document.createElement("div");
this._modalElement.className = "ModalContainer";
document.body.appendChild(this._modalElement);
}
componentWillUnmount() {
if (this._modalElement.parentNode) {
this._modalElement.parentNode.removeChild(this._modalElement);
}
}
handleDismissal = () => {
if (this.props.onClose) {
this.props.onClose();
}
};
_modalComponent() {
const className = cx(
this.props.className,
...modalSizes
.filter(type => this.props[type])
.map(type => `Modal--${type}`),
);
return (
<MaybeOnClickOutsideWrapper
backdropElement={this._modalElement}
handleDismissal={this.handleDismissal}
closeOnClickOutside={this.props.closeOnClickOutside}
>
<div
className={cx(className, "relative bg-white rounded")}
role="dialog"
>
{getModalContent({
...this.props,
fullPageModal: false,
// if there is a form then its a form modal, or if there's a form
// modal prop use that
formModal: !!this.props.form || this.props.formModal,
})}
</div>
</MaybeOnClickOutsideWrapper>
);
}
render() {
const {
enableMouseEvents,
backdropClassName,
isOpen,
style,
enableTransition,
} = this.props;
const backdropClassnames =
"flex justify-center align-center fixed top left bottom right";
return (
<SandboxedPortal
container={this._modalElement}
enableMouseEvents={enableMouseEvents}
>
<TransitionGroup
appear={enableTransition}
enter={enableTransition}
exit={enableTransition}
>
{isOpen && (
<CSSTransition
key="modal"
classNames="Modal"
timeout={{
appear: 250,
enter: 250,
exit: 250,
}}
>
<div
className={cx(backdropClassName, backdropClassnames)}
style={style}
>
{this._modalComponent()}
</div>
</CSSTransition>
)}
</TransitionGroup>
</SandboxedPortal>
);
}
}
export { default } from "./Modal";
// eslint-disable-next-line import/no-default-export -- deprecated usage
export { Modal as default } from "./Modal";
import React from "react";
import _ from "underscore";
import ModalContent from "metabase/components/ModalContent";
export const modalSizes = ["small", "medium", "wide", "tall", "fit"] as const;
export type ModalSize = typeof modalSizes[number];
export type BaseModalProps = {
children?: React.ReactNode;
className?: string;
backdropClassName?: string;
enableMouseEvents?: boolean;
enableTransition?: boolean;
closeOnClickOutside?: boolean;
noBackdrop?: boolean;
noCloseOnBackdrop?: boolean;
form?: unknown;
title?: string;
footer?: string;
};
export function getModalContent(props: any) {
if (
React.Children.count(props.children) > 1 ||
props.title != null ||
props.footer != null
) {
return <ModalContent {..._.omit(props, "className", "style")} />;
} else {
return React.Children.only(props.children);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment