'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var _extends = require('@babel/runtime/helpers/extends');
var _defineProperty = require('@babel/runtime/helpers/defineProperty');
var _slicedToArray = require('@babel/runtime/helpers/slicedToArray');
var _objectWithoutProperties = require('@babel/runtime/helpers/objectWithoutProperties');
require('core-js/modules/es.array.concat.js');
var react = require('@carbon/react');
var PropTypes = require('prop-types');
var React = require('react');
var classnames = require('classnames');
var warning = require('warning');
var uuid = require('uuid');
var Settings = require('../../constants/Settings.js');
var componentUtilityFunctions = require('../../utils/componentUtilityFunctions.js');
var Button = require('../Button/Button.js');
var deprecate = require('../../internal/deprecate.js');
var useSizeObserver = require('../../hooks/useSizeObserver.js');

function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }

var _extends__default = /*#__PURE__*/_interopDefault(_extends);
var _defineProperty__default = /*#__PURE__*/_interopDefault(_defineProperty);
var _slicedToArray__default = /*#__PURE__*/_interopDefault(_slicedToArray);
var _objectWithoutProperties__default = /*#__PURE__*/_interopDefault(_objectWithoutProperties);
var PropTypes__default = /*#__PURE__*/_interopDefault(PropTypes);
var React__default = /*#__PURE__*/_interopDefault(React);
var classnames__default = /*#__PURE__*/_interopDefault(classnames);
var warning__default = /*#__PURE__*/_interopDefault(warning);

var _excluded = ["header", "error", "sendingData", "children", "footer", "open", "className", "type", "onClose", "isFetchingData", "isFullScreen", "isLarge", "onSubmit", "iconDescription", "onClearError", "submitFailed", "invalid", "passiveModal", "testID", "testId", "selectorPrimaryFocus"];
var iotPrefix = Settings.settings.iotPrefix,
  prefix = Settings.settings.prefix;
var ComposedModalPropTypes = {
  /** Header Props
   * label: goes on top of the dialog
   * title: Heading of the dialog
   * helpText, additional information will stay at the top of the screen when scrolling dialog content
   */
  header: PropTypes__default.default.shape({
    label: PropTypes__default.default.node,
    title: PropTypes__default.default.node,
    helpText: PropTypes__default.default.node
  }),
  /** ability to add translation string to close icon */
  iconDescription: PropTypes__default.default.string,
  /** Content to render inside Modal */
  children: PropTypes__default.default.node,
  /** Footer Props
   * Either supply your own footer element or supply an object with button labels and submit handlers and we will make a footer with two buttons for you
   */
  footer: PropTypes__default.default.oneOfType([PropTypes__default.default.element.isRequired, PropTypes__default.default.shape({
    primaryButtonLabel: PropTypes__default.default.node,
    secondaryButtonLabel: PropTypes__default.default.node,
    /** should the primary button be hidden (i.e. only show Cancel) */
    isPrimaryButtonHidden: PropTypes__default.default.bool,
    /** should the primary button be disabled */
    isPrimaryButtonDisabled: PropTypes__default.default.bool
  })]),
  /** NEW PROP: Type of dialog, affects colors, styles of dialog */
  type: PropTypes__default.default.oneOf(['warn', 'normal']),
  /** NEW PROP: Whether this particular dialog needs to be very large */
  isLarge: PropTypes__default.default.bool,
  /** NEW PROP: Whether this particular dialog needs to be full width */
  isFullScreen: PropTypes__default.default.bool,
  /** Should the dialog be open or not */
  open: PropTypes__default.default.bool,
  // eslint-disable-line react/boolean-prop-naming
  /**   Close the dialog */
  onClose: PropTypes__default.default.func.isRequired,
  /**  Is data currently being sent to the backend */
  sendingData: PropTypes__default.default.oneOfType([PropTypes__default.default.bool, PropTypes__default.default.string]),
  /**  Is my data actively loading? */
  isFetchingData: PropTypes__default.default.bool,
  /** Form Error Details */
  error: PropTypes__default.default.string,
  /**  Clear the currently shown error, triggered if the user closes the ErrorNotification */
  onClearError: PropTypes__default.default.func,
  /** Did the form submission fail */
  submitFailed: PropTypes__default.default.bool,
  // eslint-disable-line react/boolean-prop-naming
  /** Is the form currently invalid */
  invalid: PropTypes__default.default.bool,
  // eslint-disable-line react/boolean-prop-naming
  /** Callback to submit the dialog/form */
  onSubmit: PropTypes__default.default.func,
  /** Hide the footer */
  passiveModal: PropTypes__default.default.bool,
  // TODO: remove deprecated testID in v3.
  // eslint-disable-next-line react/require-default-props
  testID: deprecate(PropTypes__default.default.string, "The 'testID' prop has been deprecated. Please use 'testId' instead."),
  /** Id that can be used for testing */
  testId: PropTypes__default.default.string,
  /**
   * Specify a CSS selector that matches the DOM element that should be
   * focused when the Modal opens
   */
  selectorPrimaryFocus: PropTypes__default.default.string
};
var defaultProps = {
  open: true,
  error: null,
  isFetchingData: false,
  sendingData: null,
  onClearError: null,
  type: null,
  footer: null,
  isFullScreen: false,
  isLarge: false,
  submitFailed: false,
  onSubmit: null,
  invalid: false,
  children: null,
  header: {},
  iconDescription: 'Close',
  passiveModal: false,
  testId: 'ComposedModal',
  selectorPrimaryFocus: '.cds--modal-header'
};

/**
 * Renders a carbon modal dialog.  This dialog adds these additional features on top of the base carbon dialog:
 *  adds header.helpText prop to explain dialog
 *  adds type prop for warning and error type dialogs
 *  adds isFullScreen prop to have the modal appear in full width using class styling
 *  adds isLarge prop for large and small class styling dialogs
 *  adds isFetchingData props for loading state
 *  adds error and dataError prop to display notification about error at bottom of dialog
 *  if submitFailed prop, it will find and scroll the failing carbon element into view
 *  shows spinner on primary dialog button if sendingData prop is true
 *
 * We also prevent the dialog from closing if you click outside it.
 *
 */
var ComposedModal = function ComposedModal(_ref) {
  var header = _ref.header,
    error = _ref.error,
    sendingData = _ref.sendingData,
    children = _ref.children,
    footer = _ref.footer,
    open = _ref.open,
    className = _ref.className,
    type = _ref.type,
    onClose = _ref.onClose,
    isFetchingData = _ref.isFetchingData,
    isFullScreen = _ref.isFullScreen,
    isLarge = _ref.isLarge,
    onSubmit = _ref.onSubmit,
    iconDescription = _ref.iconDescription,
    onClearError = _ref.onClearError,
    submitFailed = _ref.submitFailed,
    invalid = _ref.invalid,
    passiveModal = _ref.passiveModal,
    testID = _ref.testID,
    testId = _ref.testId,
    selectorPrimaryFocus = _ref.selectorPrimaryFocus,
    props = _objectWithoutProperties__default.default(_ref, _excluded);
  React.useEffect(function () {
    if (passiveModal && (footer || onSubmit)) {
      warning__default.default(false, 'You have set passiveModal to true, but also passed a footer or onSubmit handler.  Your footer will not be rendered.') ;
    }
  }, [footer, onSubmit, passiveModal]);
  React.useEffect(function () {
    if (invalid && submitFailed) {
      componentUtilityFunctions.scrollErrorIntoView(true);
    }
  }, [invalid, submitFailed]);
  var handleClearError = function handleClearError() {
    if (onClearError) {
      onClearError();
    }
  };
  var _useState = React.useState(0),
    _useState2 = _slicedToArray__default.default(_useState, 2),
    modalExtrasHeight = _useState2[0],
    setModalExtrasHeight = _useState2[1];

  /** TODO: This is needed to keep the ComposedModal from closing if you click outside it this supports our dialogs on top of dialogs issue */
  var doNotClose = function doNotClose() {
    return false;
  };
  var modalRef = React.useRef(null);
  var modalBodyRef = React.useRef(null);
  var _useSizeObserver = useSizeObserver({
      ref: modalRef
    }),
    _useSizeObserver2 = _slicedToArray__default.default(_useSizeObserver, 1),
    modalSize = _useSizeObserver2[0];
  var _useSizeObserver3 = useSizeObserver({
      ref: modalBodyRef
    }),
    _useSizeObserver4 = _slicedToArray__default.default(_useSizeObserver3, 1),
    modalBodySize = _useSizeObserver4[0];
  React.useLayoutEffect(function () {
    if (modalRef.current && modalBodyRef.current) {
      var _modalRef$current$que, _modalRef$current$que2, _modalRef$current$que3, _modalRef$current$que4;
      var headerHeight = (_modalRef$current$que = (_modalRef$current$que2 = modalRef.current.querySelector(".".concat(prefix, "--modal-header"))) === null || _modalRef$current$que2 === void 0 || (_modalRef$current$que2 = _modalRef$current$que2.getBoundingClientRect()) === null || _modalRef$current$que2 === void 0 ? void 0 : _modalRef$current$que2.height) !== null && _modalRef$current$que !== void 0 ? _modalRef$current$que : 0;
      var footerHeight = (_modalRef$current$que3 = (_modalRef$current$que4 = modalRef.current.querySelector(".".concat(prefix, "--modal-footer"))) === null || _modalRef$current$que4 === void 0 || (_modalRef$current$que4 = _modalRef$current$que4.getBoundingClientRect()) === null || _modalRef$current$que4 === void 0 ? void 0 : _modalRef$current$que4.height) !== null && _modalRef$current$que3 !== void 0 ? _modalRef$current$que3 : 0;
      setModalExtrasHeight(Math.floor(headerHeight + footerHeight));
    }
  }, [modalSize.height, modalBodySize.height]);
  var label = header.label,
    title = header.title,
    helpText = header.helpText;
  // First check for dataErrors as they are worse than form errors
  return isFetchingData ? /*#__PURE__*/React__default.default.createElement(react.Loading, null) : /*#__PURE__*/React__default.default.createElement(react.ComposedModal, _extends__default.default({}, props, {
    selectorPrimaryFocus: selectorPrimaryFocus,
    forwardedRef: modalRef
    // TODO: remove deprecated testID in v3.
    ,
    "data-testid": testID || testId,
    open: open,
    onClose: doNotClose,
    "data-floating-menu-container": true // TODO: Can remove once this issue is fixed: https://github.com/carbon-design-system/carbon/issues/6662
    ,
    className: classnames__default.default(className, _defineProperty__default.default(_defineProperty__default.default({}, "".concat(iotPrefix, "--composed-modal--large"), isLarge), "".concat(iotPrefix, "--composed-modal--full-screen"), isFullScreen), "".concat(iotPrefix, "--composed-modal")),
    style: modalExtrasHeight > 0 ? {
      '--modal-extras-height': modalExtrasHeight
    } : undefined
  }), /*#__PURE__*/React__default.default.createElement(react.ModalHeader, {
    label: label,
    title: title,
    closeModal: onClose,
    buttonOnClick: onClose,
    iconDescription: iconDescription
  }, helpText ? /*#__PURE__*/React__default.default.createElement("p", {
    className: "".concat(prefix, "--modal-content__text")
  }, helpText) : null), children ? /*#__PURE__*/React__default.default.createElement(react.ModalBody, {
    ref: modalBodyRef,
    className: classnames__default.default(_defineProperty__default.default({}, "".concat(iotPrefix, "--composed-modal__body--small-margin-bottom"), error)),
    style: {
      maxHeight: "calc(100vh - ".concat(modalExtrasHeight, "px)")
    }
  }, children) : null, error ? /*#__PURE__*/React__default.default.createElement(react.InlineNotification, {
    key: uuid.v4() // regenerate the inline notification after they click on close button
    ,
    lowContrast: true,
    title: error,
    subtitle: "",
    kind: "error",
    onCloseButtonClick: handleClearError,
    className: "".concat(iotPrefix, "--composed-modal--inline-notification"),
    "data-testid": "".concat(testID || testId, "-notification")
  }) : null, !passiveModal ? /*#__PURE__*/React__default.default.createElement(react.ModalFooter, {
    className: "".concat(iotPrefix, "--composed-modal-footer")
  }, /*#__PURE__*/React__default.default.isValidElement(footer) ? footer : /*#__PURE__*/React__default.default.createElement(React.Fragment, null, /*#__PURE__*/React__default.default.createElement(Button, {
    kind: "secondary",
    onClick: onClose,
    testId: "".concat(testID || testId, "-modal-secondary-button")
  }, footer && footer.secondaryButtonLabel || 'Cancel'), !(footer !== null && footer !== void 0 && footer.isPrimaryButtonHidden) ? /*#__PURE__*/React__default.default.createElement(Button, {
    disabled: footer === null || footer === void 0 ? void 0 : footer.isPrimaryButtonDisabled,
    kind: type === 'warn' ? 'danger' : 'primary',
    loading: typeof sendingData === 'boolean' && sendingData || typeof sendingData === 'string',
    onClick: onSubmit,
    testId: "".concat(testID || testId, "-modal-").concat(type === 'warn' ? 'danger' : 'primary', "-button")
  }, footer && footer.primaryButtonLabel || 'Save') : null)) : null);
};
ComposedModal.propTypes = ComposedModalPropTypes;
ComposedModal.defaultProps = defaultProps;
ComposedModal.__docgenInfo = {
  "description": "Renders a carbon modal dialog.  This dialog adds these additional features on top of the base carbon dialog:\n adds header.helpText prop to explain dialog\n adds type prop for warning and error type dialogs\n adds isFullScreen prop to have the modal appear in full width using class styling\n adds isLarge prop for large and small class styling dialogs\n adds isFetchingData props for loading state\n adds error and dataError prop to display notification about error at bottom of dialog\n if submitFailed prop, it will find and scroll the failing carbon element into view\n shows spinner on primary dialog button if sendingData prop is true\n\nWe also prevent the dialog from closing if you click outside it.",
  "methods": [],
  "displayName": "ComposedModal",
  "props": {
    "open": {
      "defaultValue": {
        "value": "true",
        "computed": false
      },
      "description": "Should the dialog be open or not",
      "type": {
        "name": "bool"
      },
      "required": false
    },
    "error": {
      "defaultValue": {
        "value": "null",
        "computed": false
      },
      "description": "Form Error Details",
      "type": {
        "name": "string"
      },
      "required": false
    },
    "isFetchingData": {
      "defaultValue": {
        "value": "false",
        "computed": false
      },
      "description": "Is my data actively loading?",
      "type": {
        "name": "bool"
      },
      "required": false
    },
    "sendingData": {
      "defaultValue": {
        "value": "null",
        "computed": false
      },
      "description": "Is data currently being sent to the backend",
      "type": {
        "name": "union",
        "value": [{
          "name": "bool"
        }, {
          "name": "string"
        }]
      },
      "required": false
    },
    "onClearError": {
      "defaultValue": {
        "value": "null",
        "computed": false
      },
      "description": "Clear the currently shown error, triggered if the user closes the ErrorNotification",
      "type": {
        "name": "func"
      },
      "required": false
    },
    "type": {
      "defaultValue": {
        "value": "null",
        "computed": false
      },
      "description": "NEW PROP: Type of dialog, affects colors, styles of dialog",
      "type": {
        "name": "enum",
        "value": [{
          "value": "'warn'",
          "computed": false
        }, {
          "value": "'normal'",
          "computed": false
        }]
      },
      "required": false
    },
    "footer": {
      "defaultValue": {
        "value": "null",
        "computed": false
      },
      "description": "Footer Props\nEither supply your own footer element or supply an object with button labels and submit handlers and we will make a footer with two buttons for you",
      "type": {
        "name": "union",
        "value": [{
          "name": "element"
        }, {
          "name": "shape",
          "value": {
            "primaryButtonLabel": {
              "name": "node",
              "required": false
            },
            "secondaryButtonLabel": {
              "name": "node",
              "required": false
            },
            "isPrimaryButtonHidden": {
              "name": "bool",
              "description": "should the primary button be hidden (i.e. only show Cancel)",
              "required": false
            },
            "isPrimaryButtonDisabled": {
              "name": "bool",
              "description": "should the primary button be disabled",
              "required": false
            }
          }
        }]
      },
      "required": false
    },
    "isFullScreen": {
      "defaultValue": {
        "value": "false",
        "computed": false
      },
      "description": "NEW PROP: Whether this particular dialog needs to be full width",
      "type": {
        "name": "bool"
      },
      "required": false
    },
    "isLarge": {
      "defaultValue": {
        "value": "false",
        "computed": false
      },
      "description": "NEW PROP: Whether this particular dialog needs to be very large",
      "type": {
        "name": "bool"
      },
      "required": false
    },
    "submitFailed": {
      "defaultValue": {
        "value": "false",
        "computed": false
      },
      "description": "Did the form submission fail",
      "type": {
        "name": "bool"
      },
      "required": false
    },
    "onSubmit": {
      "defaultValue": {
        "value": "null",
        "computed": false
      },
      "description": "Callback to submit the dialog/form",
      "type": {
        "name": "func"
      },
      "required": false
    },
    "invalid": {
      "defaultValue": {
        "value": "false",
        "computed": false
      },
      "description": "Is the form currently invalid",
      "type": {
        "name": "bool"
      },
      "required": false
    },
    "children": {
      "defaultValue": {
        "value": "null",
        "computed": false
      },
      "description": "Content to render inside Modal",
      "type": {
        "name": "node"
      },
      "required": false
    },
    "header": {
      "defaultValue": {
        "value": "{}",
        "computed": false
      },
      "description": "Header Props\nlabel: goes on top of the dialog\ntitle: Heading of the dialog\nhelpText, additional information will stay at the top of the screen when scrolling dialog content",
      "type": {
        "name": "shape",
        "value": {
          "label": {
            "name": "node",
            "required": false
          },
          "title": {
            "name": "node",
            "required": false
          },
          "helpText": {
            "name": "node",
            "required": false
          }
        }
      },
      "required": false
    },
    "iconDescription": {
      "defaultValue": {
        "value": "'Close'",
        "computed": false
      },
      "description": "ability to add translation string to close icon",
      "type": {
        "name": "string"
      },
      "required": false
    },
    "passiveModal": {
      "defaultValue": {
        "value": "false",
        "computed": false
      },
      "description": "Hide the footer",
      "type": {
        "name": "bool"
      },
      "required": false
    },
    "testId": {
      "defaultValue": {
        "value": "'ComposedModal'",
        "computed": false
      },
      "description": "Id that can be used for testing",
      "type": {
        "name": "string"
      },
      "required": false
    },
    "selectorPrimaryFocus": {
      "defaultValue": {
        "value": "'.cds--modal-header'",
        "computed": false
      },
      "description": "Specify a CSS selector that matches the DOM element that should be\nfocused when the Modal opens",
      "type": {
        "name": "string"
      },
      "required": false
    },
    "onClose": {
      "description": "Close the dialog",
      "type": {
        "name": "func"
      },
      "required": true
    },
    "testID": {
      "description": "",
      "type": {
        "name": "custom",
        "raw": "deprecate(\n  PropTypes.string,\n  `The 'testID' prop has been deprecated. Please use 'testId' instead.`\n)"
      },
      "required": false
    }
  }
};
var ComposedModal$1 = ComposedModal;

exports.ComposedModalPropTypes = ComposedModalPropTypes;
exports.default = ComposedModal$1;
