All files / src index.js

100% Statements 59/59
95% Branches 19/20
100% Functions 11/11
100% Lines 58/58
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143                    1x                               5x       5x 5x         5x 5x 5x 5x       8x 8x 8x 8x 8x         8x       3x 3x 3x 3x 3x   3x 3x 3x 1x         4x 4x 3x 3x 2x           5x 5x 5x 5x 5x 5x           9x 9x 9x 9x 9x 9x 3x 3x 3x 3x 3x 3x 3x   6x 6x 3x 3x 3x 3x   6x 5x                               5x 5x             13x          
// @flow
 
import React, {Component} from 'react';
import {render, unmountComponentAtNode} from 'react-dom';
import invariant from 'invariant';
import noop from 'lodash/noop';
import modalComponentHOC from './HOC';
import {activeModals} from './utils';
import type {ModalProps} from './types/ModalProps';
 
const ESC_CODE = 27;
 
export default class Modal extends Component {
    static defaultProps = {
        parentSelector: 'body',
        isOpen: false,
        className: '',
        animation: '',
        appSelector: 'body [data-reactroot]',
        style: {},
        onRequestClose: noop,
        styleName: '',
        isAnimated: false
    };
 
    componentWillMount() {
        document.addEventListener('keydown', this.onKeyDown);
    }
 
    componentDidMount() {
        const {props} = this;
        const initialProps = {
            ...props,
            styleName: props.className,
            className: ''
        };
        this.component = modalComponentHOC(onChange => (this.updateComponent = onChange), initialProps);
        this.root = this.getRoot();
        this.renderContainer();
        activeModals.pushIfActive(this);
    }
 
    componentWillUpdate(nextProps: Object) {
        const {isOpen} = this.props;
        const {isOpen: nextIsOPen} = nextProps;
        Eif (isOpen !== nextIsOPen) {
            activeModals.toggle(this);
            this.toggleAppFixed(nextIsOPen);
        }
    }
 
    componentDidUpdate() {
        this.updateComponent(this.props);
    }
 
    componentWillUnmount() {
        const {root} = this;
        const parent = root.parentNode;
        const {isOpen} = this.props;
        unmountComponentAtNode(root);
        parent.removeChild(root);
 
        document.removeEventListener('keydown', this.onKeyDown);
        activeModals.pop(this);
        if (isOpen) {
            this.toggleAppFixed(false);
        }
    }
 
    onKeyDown = (e: KeyboardEvent) => {
        const {isOpen} = this.props;
        if (e.keyCode === ESC_CODE && isOpen) {
            const {onRequestClose} = this.props;
            if (activeModals.isOnTop(this)) {
                onRequestClose(e);
            }
        }
    };
 
    getRoot() {
        const {parentSelector} = this.props;
        const parent = document.querySelector(parentSelector);
        invariant(parent, 'invalid parent selector');
        const container = document.createElement('div');
        parent.appendChild(container);
        return container;
    }
 
    props: ModalProps;
 
    toggleAppFixed(isOpen: boolean) {
        const {appSelector} = this.props;
        const app = document.querySelector(appSelector);
        invariant(app, 'invalid app selector');
        const {documentElement} = document;
        invariant(documentElement, 'document should be initialized');
        if (isOpen) {
            const appStyles = getComputedStyle(app);
            this.oldPosition = appStyles.position;
            this.oldScrollTop = Math.abs(parseInt(appStyles.top, 10)) || documentElement.scrollTop;
            app.style.position = 'fixed';
            app.style.left = '0';
            app.style.right = '0';
            app.style.top = `-${this.oldScrollTop}px`;
        } else {
            const {oldPosition, oldScrollTop} = this;
            if (oldPosition !== undefined && oldScrollTop !== undefined) {
                app.style.position = oldPosition;
                app.style.left = 'auto';
                app.style.right = 'auto';
                documentElement.scrollTop = oldScrollTop;
            }
            if(activeModals.isEmpty()) {
                app.style.top = ''
            }
        }
    }
 
    component: any;
 
    root: any;
 
    oldPosition: string;
 
    oldScrollTop: number;
 
    updateComponent: Function;
 
    renderContainer() {
        const Container = this.component;
        render(
            <Container />,
            this.root
        );
    }
 
    render() {
        return null;
    }
}
 
export {default as ContextTypes} from './types/contextTypes';