diff --git a/core/components/molecules/modal/Modal.tsx b/core/components/molecules/modal/Modal.tsx index 296bcaebf4..db24db90a5 100644 --- a/core/components/molecules/modal/Modal.tsx +++ b/core/components/molecules/modal/Modal.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import classNames from 'classnames'; -const { useEffect, useState } = React; import Backdrop from '@/components/atoms/backdrop'; import OutsideClick from '@/components/atoms/outsideClick'; import { BaseProps, extractBaseProps } from '@/utils/types'; @@ -36,67 +35,125 @@ export interface ModalProps extends BaseProps { children?: React.ReactNode; } -const Modal = (props: ModalProps) => { - const { - dimension = 'small', - children, - backdropClose, - backdrop, - className - } = props; - const [open, setOpen] = useState(props.open); - const [animate, setAnimate] = useState(false); - - const classes = classNames({ - Modal: true, - [`Modal--${dimension}`]: dimension, - 'Modal--open': open, - 'Modal-animation--open': animate, - 'Modal-animation--close': !animate, - }, className); - - const baseProps = extractBaseProps(props); - - useEffect(() => { - if (props.open) { - setOpen(true); - setAnimate(true); +interface ModalState { + open: boolean; + animate: boolean; + zIndex?: number; +} + +class Modal extends React.Component { + modalRef = React.createRef(); + element: Element; + + constructor(props: ModalProps) { + super(props); + + let element = document.querySelector('.Modal-wrapper'); + if (element === null) { + element = document.createElement('div'); + element.classList.add('Modal-wrapper'); + document.body.appendChild(element); } - if (!props.open) { - setTimeout(() => { - setOpen(false); - }, 120); - setAnimate(false); + + this.element = element; + + this.state = { + open: props.open, + animate: props.open, + }; + } + + componentDidUpdate(prevProps: ModalProps) { + if (prevProps.open !== this.props.open) { + if (this.props.open) { + const zIndex = this.getUpdatedZIndex(); + this.setState({ + zIndex, + open: true, + animate: true + }); + } else { + this.setState({ + animate: false, + }); + setTimeout(() => { + this.setState({ + open: false + }); + }, 120); + } } - }, [props.open]); + } + + getUpdatedZIndex = () => { + if (this.element === null) return; + + const elements = this.element.querySelectorAll('.Modal-container'); + if (elements.length <= 1) return; + + const siblings = Array.from(elements).filter(el => el !== this.modalRef.current); + let zIndex = -1; - const ModalContainer = ( -
-
- {children} + siblings.forEach(element => { + if (element.classList.contains('Modal-container--open')) { + const prevZIndex = parseInt(window.getComputedStyle(element).zIndex || '0', 10); + zIndex = Math.max(zIndex, prevZIndex + 10); + } + }); + + return zIndex > 0 ? zIndex : undefined; + } + + render() { + const { animate, open, zIndex } = this.state; + const { className, dimension, backdropClose } = this.props; + + const classes = classNames({ + Modal: true, + [`Modal--${dimension}`]: dimension, + 'Modal--open': open, + 'Modal-animation--open': animate, + 'Modal-animation--close': !animate, + }, className); + + const ContainerClass = classNames({ + ['Modal-container']: true, + ['Modal-container--open']: open, + }); + + const baseProps = extractBaseProps(this.props); + + const ModalContainer = ( +
+
+ {this.props.children} +
-
- ); - - const ModalWrapper = backdrop ? ( - open && backdropClose(event, 'OutsideClick')}> - {ModalContainer} - - ) : ModalContainer; - - const WrapperElement = ReactDOM.createPortal( - ModalWrapper, - document.body - ); - - return ( -
- {WrapperElement} - -
- ); -}; - -Modal.displayName = 'Modal'; + ); + + const ModalWrapper = this.props.backdrop ? ( + open && backdropClose(event, 'OutsideClick')}> + {ModalContainer} + + ) : ModalContainer; + + const WrapperElement = ReactDOM.createPortal( + ModalWrapper, + this.element + ); + + return ( +
+ {WrapperElement} + +
+ ); + } +} export default Modal; diff --git a/core/components/molecules/popover/Popover.tsx b/core/components/molecules/popover/Popover.tsx index cee3414e67..bb79eb4f85 100644 --- a/core/components/molecules/popover/Popover.tsx +++ b/core/components/molecules/popover/Popover.tsx @@ -120,7 +120,7 @@ export const Popover = (props: PopoverProps) => { }, className); const PopoverWrapper = ( -
+
{children}
); diff --git a/core/utils/PopperWrapper.tsx b/core/utils/PopperWrapper.tsx index 358e6453f5..00b23b2bd2 100644 --- a/core/utils/PopperWrapper.tsx +++ b/core/utils/PopperWrapper.tsx @@ -46,6 +46,7 @@ interface Props { interface IState { open: boolean; + zIndex?: number; mouseLeaveDelay: number; mouseEnterDelay: number; } @@ -134,10 +135,19 @@ class PopperWrapper extends React.Component { } } - public componentDidUpdate() { + public componentDidUpdate(prevProps: Props) { const { on = 'click', closeOnBackdropClick = true } = this.props; const { open } = this.props; + if (prevProps.open !== this.props.open && this.props.open) { + const triggerElement = this.findDOMNode(this.triggerRef); + const zIndex = this.getZIndexForLayer(triggerElement); + + this.setState({ + zIndex: zIndex === undefined ? zIndex : zIndex + 1 + }); + } + if (on === 'click' && open && closeOnBackdropClick) { document.addEventListener('mousedown', this.doesNodeContainClick); } else if (on === 'click' && !open && closeOnBackdropClick) { @@ -145,6 +155,19 @@ class PopperWrapper extends React.Component { } } + public getZIndexForLayer(node: HTMLElement | null) { + if (node === null) { + return; + } + + const layerNode = node.closest('[data-layer]') || document.body; + const zIndex = + layerNode === document.body + ? 'auto' + : parseInt(window.getComputedStyle(layerNode).zIndex || '0', 10); + return zIndex === 'auto' || isNaN(zIndex) ? undefined : zIndex; + } + public getTriggerElement(trigger: React.ReactElement, ref: React.Ref, on: actionType) { const options = on === 'hover' ? { @@ -219,7 +242,7 @@ class PopperWrapper extends React.Component { > {({ ref, style, placement }) => { const newStyle = offset ? this.getUpdatedStyle(style, placement, offset) : style; - return this.getChildrenElement(children, ref, placement, newStyle); + return this.getChildrenElement(children, ref, placement, { ...newStyle, zIndex: this.state.zIndex }); }} ), @@ -229,7 +252,7 @@ class PopperWrapper extends React.Component { {({ ref, style, placement }) => { const newStyle = offset ? this.getUpdatedStyle(style, placement, offset) : style; - return this.getChildrenElement(children, ref, placement, newStyle); + return this.getChildrenElement(children, ref, placement, { ...newStyle, zIndex: this.state.zIndex }); }} )} diff --git a/css/src/components/backdrop.css b/css/src/components/backdrop.css index 6878f1af7b..cca4d7493c 100644 --- a/css/src/components/backdrop.css +++ b/css/src/components/backdrop.css @@ -17,7 +17,7 @@ top: 0; display: none; align-items: center; - z-index: 999999; + z-index: 1000; overflow: auto; flex-direction: column; justify-content: center; diff --git a/css/src/components/modal.css b/css/src/components/modal.css index de1bb879b6..b355ba1d69 100644 --- a/css/src/components/modal.css +++ b/css/src/components/modal.css @@ -26,7 +26,7 @@ flex-direction: row; justify-content: center; width: 100vw; - z-index: 99999999; + z-index: 1500; top: 0; } diff --git a/css/src/components/popover.css b/css/src/components/popover.css index da96e096e4..fd6ff10125 100644 --- a/css/src/components/popover.css +++ b/css/src/components/popover.css @@ -1,7 +1,7 @@ .Popover { border-radius: var(--spacing-m); position: absolute; - z-index: 999999; + z-index: 500; background: var(--white); box-shadow: var(--shadow-l); } diff --git a/css/src/components/tooltip.css b/css/src/components/tooltip.css index 7d981f396b..302fcd0f49 100644 --- a/css/src/components/tooltip.css +++ b/css/src/components/tooltip.css @@ -4,7 +4,7 @@ padding: var(--spacing) var(--spacing-2); border-radius: var(--spacing-m); position: absolute; - z-index: 999999; + z-index: 500; background: var(--inverse); overflow: hidden; box-sizing: border-box;