import _extends from "@babel/runtime/helpers/extends";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
// Copyright 1999-2020. Plesk International GmbH. All rights reserved.

/* eslint-disable react/no-deprecated */
import React, { createElement, Component, cloneElement, isValidElement, useRef, forwardRef, useImperativeHandle } from 'react';
import { getIn, setIn } from './utils';
import classNames from 'classnames';
import Measure from 'react-measure';
import { CLS_PREFIX } from '../../constants';
import Button from '../Button';
import FormField from '../FormField';
import Section from '../Section';
import Translate from '../Translate';
import FormContext from './FormContext';
import VerticalContext, { VERTICAL_BREAKPOINT } from '../VerticalContext';
import DisabledContext from './DisabledContext';
import { safeInvoke, mergeRefs } from '../utils';

class Form extends Component {
  constructor(...args) {
    super(...args);

    _defineProperty(this, "state", {
      values: this.props.values,
      vertical: this.props.vertical || false,
      requiredFields: [],
      formContext: {
        getValue: (name, def) => {
          if (this.state.values) {
            return getIn(this.state.values, name, def);
          }

          return def;
        },
        setValue: (name, value) => {
          this.setState(({
            values
          }) => {
            if (values) {
              return {
                values: setIn(values, name, value)
              };
            }

            return null;
          });
          safeInvoke(this.props.onFieldChange, name, value);
        },
        getErrors: name => {
          if (this.props.errors) {
            return getIn(this.props.errors, name, {});
          }

          return {};
        },
        registerField: (name, field) => {
          if (name) {
            this.fields[name] = field;
          }
        },
        unregisterField: name => {
          if (name) {
            delete this.fields[name];
          }
        },
        setRequiredField: (name, required) => {
          this.setState(prevState => {
            if (required) {
              return {
                requiredFields: [...prevState.requiredFields, name]
              };
            }

            return {
              requiredFields: prevState.requiredFields.filter(v => v !== name)
            };
          });
        },
        getRequiredMark: () => this.props.requiredMark
      }
    });

    _defineProperty(this, "fields", {});

    _defineProperty(this, "handleSubmit", e => {
      e.preventDefault();

      if (this.props.onSubmit && this.state.values) {
        this.props.onSubmit(this.state.values, false);
      }
    });

    _defineProperty(this, "handleSubmitClick", e => {
      if (e.currentTarget.type === 'submit') {
        return;
      }

      if (this.props.onSubmit && this.state.values) {
        this.props.onSubmit(this.state.values, false);
      }
    });

    _defineProperty(this, "handleApplyClick", e => {
      if (e.currentTarget.type === 'submit') {
        return;
      }

      if (this.props.onSubmit && this.state.values) {
        this.props.onSubmit(this.state.values, true);
      }
    });

    _defineProperty(this, "handleResize", ({
      bounds
    }) => {
      const {
        vertical
      } = this.props;

      if (vertical !== undefined) {
        this.setState({
          vertical
        });
        return;
      }

      this.setState({
        vertical: bounds.width < VERTICAL_BREAKPOINT
      });
    });

    _defineProperty(this, "renderForm", ({
      actionButtons
    } = {}) => {
      const {
        innerRef,
        baseClassName,
        className,
        children,
        requiredMark,
        hideRequiredLegend,
        values,
        errors,
        submitButton,
        applyButton,
        cancelButton,
        onFieldChange,
        onSubmit,
        additionalButtons,
        footerClassName,
        state,
        render,
        vertical,
        ...props
      } = this.props;
      return /*#__PURE__*/React.createElement(FormContext.Provider, {
        value: this.state.formContext
      }, /*#__PURE__*/React.createElement(VerticalContext.Provider, {
        value: this.state.vertical
      }, /*#__PURE__*/React.createElement(DisabledContext.Provider, {
        value: !!state
      }, /*#__PURE__*/React.createElement(Measure, {
        onResize: this.handleResize,
        bounds: true
      }, ({
        measureRef
      }) => /*#__PURE__*/React.createElement("form", _extends({
        ref: mergeRefs(measureRef, innerRef || null),
        className: classNames(baseClassName, {
          ["".concat(baseClassName, "--has-required")]: this.hasRequiredField()
        }, className),
        onSubmit: this.handleSubmit
      }, props), children, actionButtons, /*#__PURE__*/React.createElement("input", {
        type: "image",
        src: "data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=",
        style: {
          border: 0,
          height: 0,
          width: 0,
          position: 'absolute'
        }
      }))))));
    });

    _defineProperty(this, "renderActionButtons", () => {
      const {
        submitButton,
        applyButton,
        cancelButton,
        state,
        additionalButtons,
        baseClassName,
        footerClassName
      } = this.props;
      return /*#__PURE__*/React.createElement(Section, {
        className: classNames("".concat(baseClassName, "__footer"), footerClassName)
      }, /*#__PURE__*/React.createElement(FormField, null, this.renderSubmitButton({
        submitButton,
        state
      }), this.renderApplyButton({
        applyButton,
        state
      }), this.renderAdditionalButtons({
        additionalButtons
      }), this.renderCancelButton({
        cancelButton,
        state
      })));
    });
  }

  static getDerivedStateFromProps(props, state) {
    const {
      values,
      errors,
      requiredMark
    } = props;
    const {
      prevValues,
      prevErrors,
      prevRequiredMark
    } = state;
    const nextState = {};

    if (values !== prevValues) {
      nextState.values = values;
    }

    if (values !== prevValues || errors !== prevErrors || requiredMark !== prevRequiredMark) {
      nextState.formContext = { ...state.formContext
      };
    }

    nextState.prevValues = values;
    nextState.prevErrors = errors;
    nextState.prevRequiredMark = requiredMark;
    return nextState;
  }

  componentDidUpdate(prevProps) {
    const {
      errors
    } = this.props;

    if (errors !== prevProps.errors) {
      let field;
      let fieldErrors;
      Object.keys(this.fields).every(name => {
        if (errors && errors[name] && typeof errors[name] === 'object' && Object.keys(errors[name]).length > 0) {
          field = this.fields[name];
          fieldErrors = errors[name];
          return false;
        }

        return true;
      });

      if (field) {
        field.focus(fieldErrors);
      }
    }
  }

  hasRequiredField() {
    return this.props.requiredMark && this.state.requiredFields.length > 0;
  }

  renderSubmitButton({
    submitButton,
    state
  }) {
    if (!submitButton) {
      return null;
    }

    return /*#__PURE__*/React.createElement(Button, _extends({
      size: "lg",
      intent: "primary",
      state: state === 'submit' ? 'loading' : undefined,
      disabled: !!state,
      onClick: this.handleSubmitClick // eslint-disable-next-line react/no-children-prop
      ,
      children: /*#__PURE__*/React.createElement(Translate, {
        content: "Form.submitButton",
        fallback: "OK"
      })
    }, submitButton));
  }

  renderApplyButton({
    applyButton,
    state
  }) {
    if (!applyButton) {
      return null;
    }

    return /*#__PURE__*/createElement(Button, {
      size: 'lg',
      intent: 'primary',
      state: state === 'apply' ? 'loading' : undefined,
      disabled: !!state,
      onClick: this.handleApplyClick,
      children: /*#__PURE__*/React.createElement(Translate, {
        content: "Form.applyButton",
        fallback: "Apply"
      }),
      ...applyButton
    });
  }

  renderCancelButton({
    cancelButton,
    state
  }) {
    if (!cancelButton) {
      return null;
    }

    return /*#__PURE__*/createElement(Button, {
      size: 'lg',
      state: state === 'cancel' ? 'loading' : undefined,
      disabled: !!state,
      children: /*#__PURE__*/React.createElement(Translate, {
        content: "Form.cancelButton",
        fallback: "Cancel"
      }),
      ...cancelButton
    });
  }

  renderAdditionalButtons({
    additionalButtons
  }) {
    const {
      baseClassName
    } = this.props;

    if (Array.isArray(additionalButtons)) {
      return additionalButtons.map(element => {
        if ( /*#__PURE__*/isValidElement(element)) {
          const props = {
            className: classNames(element.props.className, "".concat(baseClassName, "__additional-button"))
          };

          if (element.type === Button) {
            props.size = 'lg';
          }

          return /*#__PURE__*/cloneElement(element, props);
        }

        return element;
      });
    }

    return null;
  }

  render() {
    const {
      render
    } = this.props;

    if (typeof render === 'function') {
      return render({
        renderForm: this.renderForm,
        renderActionButtons: this.renderActionButtons
      });
    }

    return this.renderForm({
      actionButtons: this.renderActionButtons()
    });
  }

}
/**
 * `Form` component is used for entering and submitting of user data.
 * [More details about designing of forms.](#!/Good%20Forms)
 * @since 0.0.54
 */


_defineProperty(Form, "defaultProps", {
  children: undefined,
  requiredMark: /*#__PURE__*/React.createElement("span", {
    className: "".concat(CLS_PREFIX, "form__required")
  }),
  hideRequiredLegend: false,
  values: {},
  errors: {},
  state: undefined,
  submitButton: true,
  applyButton: true,
  cancelButton: true,
  additionalButtons: [],
  onFieldChange: undefined,
  onSubmit: undefined,
  className: undefined,
  footerClassName: undefined,
  vertical: undefined,
  baseClassName: "".concat(CLS_PREFIX, "form"),
  render: undefined,
  innerRef: undefined
});

const RefForwardingForm = /*#__PURE__*/forwardRef((props, ref) => {
  const innerRef = useRef(null);
  useImperativeHandle(ref, () => ({
    submit() {
      var _innerRef$current;

      (_innerRef$current = innerRef.current) === null || _innerRef$current === void 0 ? void 0 : _innerRef$current.dispatchEvent(new Event('submit', {
        cancelable: true,
        bubbles: true
      }));
    }

  }));
  return /*#__PURE__*/React.createElement(Form, _extends({
    innerRef: innerRef
  }, props));
});
RefForwardingForm.displayName = 'Form';
export default RefForwardingForm;