var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { mapToCssModules, getOriginalBodyPadding, conditionallyUpdateScrollbar, setScrollbarWidth, omit, focusableElements, TransitionTimeouts } from './Shared/helper.js';
import CPortal from './CPortal';
import CFade from './CFade';

function noop() {}

var FadePropTypes = PropTypes.shape(CFade.propTypes);

//component - CoreUI / CModal

var CModal = function CModal(props) {
  var _useState = useState(props.show),
      isOpen = _useState[0],
      setIsOpen = _useState[1];

  var fields = useRef({
    _element: null,
    _originalBodyPadding: null,
    lastProps: {}
  }).current;

  var onOpened = function onOpened(node, isAppearing) {
    props.onOpened();
    (props.modalTransition.onEntered || noop)(node, isAppearing);
  };

  var onClosed = function onClosed(node) {
    props.onClosed();
    (props.modalTransition.onExited || noop)(node);
    destroy();
    if (fields._isMounted) {
      setIsOpen(false);
    }
  };

  var setFocus = function setFocus() {
    if (fields._dialog && fields._dialog.parentNode && typeof fields._dialog.parentNode.focus === 'function') {
      fields._dialog.parentNode.focus();
    }
  };

  var getFocusableChildren = function getFocusableChildren() {
    return fields._element.querySelectorAll(focusableElements.join(', '));
  };

  var getFocusedChild = function getFocusedChild() {
    var currentFocus = void 0;
    var focusableChildren = getFocusableChildren();
    try {
      currentFocus = document.activeElement;
    } catch (err) {
      currentFocus = focusableChildren[0];
    }
    return currentFocus;
  };

  var handleBackdropClick = function handleBackdropClick(e) {
    if (e.target === fields._mouseDownElement) {
      e.stopPropagation();
      if (!isOpen || props.backdrop !== true) return;
      var backdrop = fields._dialog ? fields._dialog.parentNode : null;
      if (backdrop && e.target === backdrop && props.toggle) {
        props.toggle(e);
      }
    }
  };

  var handleTab = function handleTab(e) {
    if (e.which !== 9) return;
    var focusableChildren = getFocusableChildren();
    var totalFocusable = focusableChildren.length;
    var currentFocus = getFocusedChild();
    var focusedIndex = 0;
    for (var i = 0; i < totalFocusable; i += 1) {
      if (focusableChildren[i] === currentFocus) {
        focusedIndex = i;
        break;
      }
    }
    if (e.shiftKey && focusedIndex === 0) {
      e.preventDefault();
      focusableChildren[totalFocusable - 1].focus();
    } else if (!e.shiftKey && focusedIndex === totalFocusable - 1) {
      e.preventDefault();
      focusableChildren[0].focus();
    }
  };

  var handleBackdropMouseDown = function handleBackdropMouseDown(e) {
    fields._mouseDownElement = e.target;
  };

  var handleEscape = function handleEscape(e) {
    if (props.show && props.keyboard && e.keyCode === 27 && props.toggle) {
      e.preventDefault();
      e.stopPropagation();
      props.toggle(e);
    }
  };

  var init = function init() {
    try {
      fields._triggeringElement = document.activeElement;
    } catch (err) {
      fields._triggeringElement = null;
    }
    fields._element = document.createElement('div');
    fields._element.setAttribute('tabindex', '-1');
    fields._element.style.position = 'relative';
    fields._element.style.zIndex = props.zIndex;
    fields._originalBodyPadding = getOriginalBodyPadding();
    conditionallyUpdateScrollbar();
    document.body.appendChild(fields._element);
    if (CModal.openCount === 0) {
      document.body.className = classNames(document.body.className, mapToCssModules('modal-open', props.cssModule));
    }
    CModal.openCount += 1;
  };

  var destroy = function destroy() {
    if (fields._element) {
      document.body.removeChild(fields._element);
      fields._element = null;
    }
    if (fields._triggeringElement) {
      if (fields._triggeringElement.focus) fields._triggeringElement.focus();
      fields._triggeringElement = null;
    }
    if (CModal.openCount <= 1) {
      var modalOpenClassName = mapToCssModules('modal-open', props.cssModule);
      var modalOpenClassNameRegex = new RegExp('(^| )' + modalOpenClassName + '( |$)');
      document.body.className = document.body.className.replace(modalOpenClassNameRegex, ' ').trim();
    }
    CModal.openCount -= 1;
    setScrollbarWidth(fields._originalBodyPadding);
  };

  var setState = function setState(oVal, nVal, setF) {
    if (nVal === oVal) return;
    setF(nVal);
  };

  // effect

  useEffect(function () {
    if (props.onEnter) {
      props.onEnter();
    }
    if (isOpen && props.autoFocus) {
      setFocus();
    }
    fields._isMounted = true;
    return function cleanup() {
      if (props.onExit) {
        props.onExit();
      }
      if (isOpen) {
        destroy();
      }
      fields._isMounted = false;
    };
  }, []);

  useEffect(function () {
    if (props.autoFocus && isOpen && !fields.lastIsOpen) {
      setFocus();
    }
    if (fields._element && fields.lastProps.zIndex !== props.zIndex) {
      fields._element.style.zIndex = props.zIndex;
    }
  }, [isOpen]);

  useEffect(function () {
    fields.lastProps = _extends({}, props);
    fields.lastIsOpen = isOpen;
    fields.firstRender = false;
  });

  // render

  var renderModalDialog = function renderModalDialog() {
    var _classNames;

    var attributes = omit(props, Object.keys(CModal.propTypes));
    var dialogBaseClass = 'modal-dialog';
    return React.createElement(
      'div',
      _extends({}, attributes, {
        className: mapToCssModules(classNames(dialogBaseClass, props.className, (_classNames = {}, _classNames['modal-' + props.size] = props.size, _classNames[dialogBaseClass + '-centered'] = props.centered, _classNames)), props.cssModule),
        role: 'document',
        ref: function ref(c) {
          fields._dialog = c;
        }
      }),
      React.createElement(
        'div',
        {
          className: mapToCssModules(classNames('modal-content', props.contentClassName), props.cssModule)
        },
        props.children
      )
    );
  };

  if (props.show && !fields.lastProps.show) {
    setState(isOpen, props.show, setIsOpen);
  }
  if (!fields.firstRender) {
    if (isOpen && !fields.lastIsOpen) {
      init();
    }
  }

  if (isOpen) {
    var cssModule = props.cssModule,
        innerRef = props.innerRef,
        wrapClassName = props.wrapClassName,
        modalClassName = props.modalClassName,
        backdropClassName = props.backdropClassName,
        _isOpen = props.show,
        backdrop = props.backdrop,
        role = props.role,
        labelledBy = props.labelledBy,
        external = props.external;


    var modalAttributes = {
      onClick: handleBackdropClick,
      onMouseDown: handleBackdropMouseDown,
      onKeyUp: handleEscape,
      onKeyDown: handleTab,
      style: { display: 'block' },
      'aria-labelledby': labelledBy,
      role: role,
      tabIndex: '-1'
    };

    var hasTransition = props.fade;

    var modalTransition = _extends({}, CFade.defaultProps, props.modalTransition, {
      baseClass: hasTransition ? props.modalTransition.baseClass : '',
      timeout: hasTransition ? props.modalTransition.timeout : 0
    });

    var backdropTransition = _extends({}, CFade.defaultProps, props.backdropTransition, {
      baseClass: hasTransition ? props.backdropTransition.baseClass : '',
      timeout: hasTransition ? props.backdropTransition.timeout : 0
    });

    var Backdrop = backdrop && (hasTransition ? React.createElement(CFade, _extends({}, backdropTransition, {
      'in': _isOpen && !!backdrop,
      cssModule: cssModule,
      className: mapToCssModules(classNames('modal-backdrop', backdropClassName), cssModule)
    })) : React.createElement('div', { className: mapToCssModules(classNames('modal-backdrop', 'show', backdropClassName), cssModule) }));

    return React.createElement(
      CPortal,
      { node: fields._element },
      React.createElement(
        'div',
        { className: mapToCssModules(wrapClassName) },
        React.createElement(
          CFade,
          _extends({}, modalAttributes, modalTransition, {
            'in': _isOpen,
            onEntered: onOpened,
            onExited: onClosed,
            cssModule: cssModule,
            className: mapToCssModules(classNames('modal', modalClassName), cssModule),
            innerRef: innerRef
          }),
          external,
          renderModalDialog()
        ),
        Backdrop
      )
    );
  }

  return null;
};

CModal.propTypes = process.env.NODE_ENV !== "production" ? {
  children: PropTypes.node,
  className: PropTypes.string,
  cssModule: PropTypes.object,
  //
  innerRef: PropTypes.oneOfType([PropTypes.object, PropTypes.func, PropTypes.string]),
  show: PropTypes.bool,
  autoFocus: PropTypes.bool,
  centered: PropTypes.bool,
  size: PropTypes.string,
  toggle: PropTypes.func,
  keyboard: PropTypes.bool,
  role: PropTypes.string,
  labelledBy: PropTypes.string,
  backdrop: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['static'])]),
  onEnter: PropTypes.func,
  onExit: PropTypes.func,
  onOpened: PropTypes.func,
  onClosed: PropTypes.func,
  wrapClassName: PropTypes.string,
  modalClassName: PropTypes.string,
  backdropClassName: PropTypes.string,
  contentClassName: PropTypes.string,
  external: PropTypes.node,
  fade: PropTypes.bool,
  zIndex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  backdropTransition: FadePropTypes,
  modalTransition: FadePropTypes
} : {};

CModal.defaultProps = {
  show: false,
  autoFocus: true,
  centered: false,
  role: 'dialog',
  backdrop: true,
  keyboard: true,
  zIndex: 1050,
  fade: true,
  onOpened: noop,
  onClosed: noop,
  modalTransition: {
    timeout: TransitionTimeouts.Modal
  },
  backdropTransition: {
    mountOnEnter: true,
    timeout: TransitionTimeouts.Fade
  }
};

CModal.openCount = 0;

export default CModal;