"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = exports.BASE_FORM_FIELD_CLASS_NAME = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireWildcard(require("react"));
var _utils = require("../utils");
var _propTypes = _interopRequireDefault(require("prop-types"));
var _classnames = _interopRequireDefault(require("classnames"));
var _constants = require("../../constants");
var _Section = require("../Section");
var _Hint = _interopRequireDefault(require("../Hint"));
var _Button = _interopRequireDefault(require("../Button"));
var _Link = _interopRequireDefault(require("../Link"));
var _Icon = _interopRequireDefault(require("../Icon"));
var _Popover = _interopRequireDefault(require("../Popover"));
var _Action = _interopRequireDefault(require("../Action"));
var _Translate = _interopRequireDefault(require("../Translate"));
var _Focuser = _interopRequireDefault(require("../Overlay/Focuser"));
var _FormContext = _interopRequireDefault(require("../Form/FormContext"));
var _VerticalContext = _interopRequireDefault(require("../VerticalContext"));
var _ScrollableElementFormContext = _interopRequireDefault(require("../Form/ScrollableElementFormContext"));
var _DisabledContext = _interopRequireDefault(require("../Form/DisabledContext"));
var _jsxRuntime = require("react/jsx-runtime");
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
// Copyright 1999-2023. Plesk International GmbH. All rights reserved.

const defaultValue = undefined;

/**
 * Simple helper to handle situations when we expecting only array
 * @param value
 * @param nonEmpty
 * @returns {Array}
 */
const getArrayHelper = function (value) {
  let nonEmpty = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  if (Array.isArray(value) && value.length > 0) {
    return value;
  }
  const result = [];
  if (nonEmpty) {
    result.push(undefined);
  }
  return result;
};
const isControlled = props => props.value !== undefined;
const isEmpty = value => !value && value !== 0 || Array.isArray(value) && value.length === 0 || !Array.isArray(value) && typeof value === 'object' && Object.values(value).filter(t => Boolean(t)).length === 0;

/**
 * @since 0.0.54
 */
class FormField extends _react.Component {
  constructor(_props) {
    var _this;
    super(_props);
    _this = this;
    /**
     * Field API provided to field
     * @param index
     * @returns {Object}
     */
    (0, _defineProperty2.default)(this, "getFieldApi", index => {
      if (!this.props.multi) {
        return this.fieldApi;
      }
      return {
        getId: () => {
          const id = this.fieldApi.getId();
          return id ? `${id}-${index}` : id;
        },
        getName: () => {
          const name = this.fieldApi.getName();
          return name ? `${name}[${index}]` : name;
        },
        getValue: def => {
          const values = this.fieldApi.getValue();
          if (values instanceof Array && values[index]) {
            return values[index];
          }
          return def;
        },
        setValue: value => {
          const values = getArrayHelper(this.fieldApi.getValue(), true);
          return this.fieldApi.setValue(values.map((v, i) => i === index ? value : v));
        },
        getErrors: () => {
          const errors = this.fieldApi.getErrors();
          if (errors instanceof Array && errors[index]) {
            return errors[index];
          }
          return {};
        },
        getRequiredMark: () => {
          if (index === 0) {
            return this.fieldApi.getRequiredMark();
          }
          return null;
        },
        getSeparator: () => /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
          className: `${this.props.baseClassName}__separator`
        }),
        isDisabled: () => this.props.disabled,
        getFullDescription: () => {
          if (index === 0) {
            return this.fieldApi.getFullDescription();
          }
          return null;
        }
      };
    });
    (0, _defineProperty2.default)(this, "focus", errors => {
      let index = 0;
      if (Array.isArray(errors)) {
        index = errors.findIndex(v => Boolean(v));
      }
      const {
        scrollableElement
      } = this.props;
      const target = this.targetRefs[index];
      if (target) {
        (0, _utils.scrollIntoView)(target, {
          boundary: scrollableElement
        });
        this.focusTimer = setTimeout(() => {
          if (target) {
            new _Focuser.default().focusFirstDescendant(target);
          }
        }, 500);
      }
    });
    /**
     * Handler for clicks on 'Add more'
     */
    (0, _defineProperty2.default)(this, "handleAddMore", () => {
      const values = getArrayHelper(this.fieldApi.getValue(), true);
      this.fieldApi.setValue([...values, defaultValue]);
    });
    /**
     * Handler for clicks on 'Remove'
     * @param index
     */
    (0, _defineProperty2.default)(this, "handleRemove", index => {
      const values = getArrayHelper(this.fieldApi.getValue());
      this.fieldApi.setValue(values.filter((value, valueIndex) => valueIndex !== index));

      // Can be used for removing errors from form properties
      (0, _utils.safeInvoke)(this.props.onRemoveRow, index);

      // Remove errors from component state
      this.setState(prevState => {
        if (prevState.errors && prevState.errors.filter) {
          return {
            errors: prevState.errors.filter((error, errorIndex) => errorIndex !== index)
          };
        }
        return {};
      });
    });
    (0, _defineProperty2.default)(this, "setTargetRef", index => node => {
      this.targetRefs[index] = node;
    });
    /**
     * Renders 'Remove' button for each row
     * @param index
     * @returns {null|React.ReactElement}
     */
    (0, _defineProperty2.default)(this, "renderRemoveButton", index => {
      const {
        multi,
        locale,
        disabled
      } = this.props;
      const value = getArrayHelper(this.fieldApi.getValue());
      const isClearable = multi && value.length === 1 && !isEmpty(value[0]);
      if (!multi || value.length < 1 || value.length === 1 && isEmpty(value[0])) {
        return null;
      }
      return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
        className: `${this.props.baseClassName}__remove`,
        icon: isClearable ? 'clean' : 'cross-mark',
        tooltip: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Translate.default, {
          namespace: "FormField",
          content: isClearable ? 'clear' : 'remove',
          fallback: isClearable ? 'Clear' : 'Remove',
          translators: locale
        }),
        onClick: () => this.handleRemove(index),
        disabled: disabled,
        ghost: true
      });
    });
    /**
     * Renders description for last row
     * @param index
     * @returns {null|React.ReactElement}
     */
    (0, _defineProperty2.default)(this, "renderDescription", index => {
      const {
        baseClassName,
        description,
        multi
      } = this.props;
      if (!description || multi && getArrayHelper(this.fieldApi.getValue()).length - 1 > index) {
        return null;
      }
      return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Hint.default, {
        component: "div",
        className: `${baseClassName}__description`,
        children: description
      });
    });
    /**
     * Renders one field
     * @param index
     * @returns {React.ReactElement}
     */
    (0, _defineProperty2.default)(this, "renderField", function () {
      let index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
      let renderAddMore = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
      const {
        baseClassName,
        className,
        id,
        name,
        value,
        onChange,
        label: formFieldLabel,
        description,
        errors,
        required,
        children,
        multi,
        disabled,
        addMoreValidator,
        onRemoveRow,
        locale,
        form,
        vertical,
        scrollableElement,
        fullDescription,
        ...props
      } = _this.props;
      const fieldApi = _this.getFieldApi(index);
      const fieldErrors = fieldApi.getErrors();
      const hasErrors = fieldErrors && Object.keys(fieldErrors).length > 0;
      let label = formFieldLabel;
      if (label && index) {
        label = '';
      }
      let innerField = typeof children === 'function' ? children(fieldApi) : children;
      if (multi) {
        innerField = /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
          className: `${baseClassName}__multi`,
          children: [innerField, ` `, _this.renderRemoveButton(index)]
        });
      }
      return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Section.SectionItem, {
        className: (0, _classnames.default)(baseClassName, {
          [`${baseClassName}--error`]: hasErrors,
          [`${baseClassName}--vertical-multi`]: multi && vertical
        }, className),
        title: label === null ? null : /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
          className: `${baseClassName}__label`,
          children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("label", {
            htmlFor: fieldApi.getId(),
            children: label
          }), fieldApi.getFullDescription(), required ? fieldApi.getRequiredMark() : null]
        }),
        vertical: vertical,
        ...props,
        children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
          ref: _this.setTargetRef(index),
          children: innerField
        }), hasErrors && Object.keys(fieldErrors).map(key => /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", {
          className: `${baseClassName}__error`,
          children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
            className: `${baseClassName}__error-icon`,
            name: "exclamation-mark-circle-filled",
            size: "12"
          }), fieldErrors[key]]
        }, key)), _this.renderDescription(index), renderAddMore && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Link.default, {
          role: "button",
          onClick: _this.handleAddMore,
          className: `${baseClassName}__add-more`,
          disabled: disabled,
          children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Translate.default, {
            namespace: "FormField",
            content: "addMore",
            fallback: "Add one more",
            translators: locale
          })
        })]
      }, index);
    });
    const {
      value: _value,
      multi: _multi
    } = this.props;
    let valueInState = _value;
    if (_multi && !valueInState) {
      valueInState = [defaultValue];
    }

    // eslint-disable-next-line react/state-in-constructor
    this.state = {
      value: valueInState,
      errors: this.props.errors
    };
    this.fieldApi = {
      getId: () => {
        if (this.props.id) {
          return this.props.id;
        }
        if (this.props.name) {
          return this.props.name.replace(/[[\]]+/g, '-').replace(/-+$/, '');
        }
        return null;
      },
      getName: () => this.props.name,
      getValues: () => {
        if (this.props.form) {
          return this.props.form.getValues();
        }
        return null;
      },
      getValue: def => {
        if (this.props.form && this.props.name) {
          return this.props.form.getValue(this.props.name, def);
        }
        return this.state.value || def;
      },
      setValue: value => {
        this.setState({
          value
        });
        if (this.props.form && this.props.name) {
          this.props.form.setValue(this.props.name, value);
        }
        (0, _utils.safeInvoke)(this.props.onChange, value);
      },
      getErrors: () => {
        if (this.props.form && this.props.name) {
          return this.props.form.getErrors(this.props.name);
        }
        return this.state.errors;
      },
      getRequiredMark: () => {
        if (this.props.form) {
          return this.props.form.getRequiredMark();
        }
        return null;
      },
      isDisabled: () => Boolean(this.props.disabled),
      getFullDescription: () => {
        if (!this.props.fullDescription) {
          return null;
        }
        return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Popover.default, {
          target: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Action.default, {
            outerProps: {
              className: `${this.props.baseClassName}__full-description`
            },
            icon: {
              name: 'info-circle',
              size: '12',
              className: `${this.props.baseClassName}__full-description-icon`
            }
          }),
          placement: "right",
          intent: "info",
          children: this.props.fullDescription
        });
      }
    };
    this.targetRefs = [];
  }
  static getDerivedStateFromProps(props, state) {
    const nextState = {};
    const {
      errors,
      form,
      required,
      name
    } = props;
    const {
      prevErrors,
      prevRequired
    } = state;
    if (isControlled(props)) {
      nextState.value = props.value;
    }
    if (errors !== prevErrors) {
      nextState.errors = errors;
    }
    if (form && required !== prevRequired) {
      form.setRequiredField(name, required);
    }
    nextState.prevErrors = errors;
    nextState.prevRequired = required;
    return nextState;
  }
  componentDidMount() {
    if (this.props.form) {
      this.props.form.setRequiredField(this.props.name, this.props.required);
      this.props.form.registerField(this.props.name, {
        focus: this.focus
      });
    }
  }
  componentWillUnmount() {
    if (this.props.form) {
      this.props.form.setRequiredField(this.props.name, false);
      this.props.form.unregisterField(this.props.name);
    }
    clearTimeout(this.focusTimer);
  }
  render() {
    if (!this.props.multi) {
      return this.renderField();
    }
    const max = this.props.multi.max || Infinity;
    let index = 0;
    const count = (this.fieldApi.getValue() || []).length || 1;
    const lines = [];
    do {
      lines.push(this.renderField(index++, count === index && count < max));
    } while (index < count);
    return lines;
  }
}
const propTypes = {
  /**
   * Field ID.
   * @since 0.0.59
   */
  id: _propTypes.default.string,
  /**
   * Field name.
   * @since 0.0.54
   */
  name: _propTypes.default.string,
  /**
   * Field value.
   * @deprecated Use [Form](#!/Form) values.
   * @since 0.0.57
   */
  value: _propTypes.default.any,
  /**
   * A callback function, can be executed when the value is changing.
   * @since 0.4.1
   */
  onChange: _propTypes.default.func,
  /**
   * Field label.
   * @since 0.0.54
   */
  label: _propTypes.default.any,
  /**
   * Field description.
   * @since 0.0.57
   */
  description: _propTypes.default.any,
  /**
   * Show long descriptions in popover after clicking on "( i )" icon
   * @since 3.14.0
   */
  fullDescription: _propTypes.default.node,
  /**
   * Field errors.
   *  @deprecated Use [Form](#!/Form) errors.
   * @since 0.0.57
   */
  errors: _propTypes.default.oneOfType([_propTypes.default.object, _propTypes.default.array]),
  /**
   * Indicates whether field is required.
   * @since 0.0.54
   */
  required: _propTypes.default.bool,
  /**
   * Content of the `FormField`.
   * @since 0.0.54
   */
  children: _propTypes.default.oneOfType([_propTypes.default.node, _propTypes.default.func]),
  /**
   * If 'true', a field will be multiple. You can also limit the number of fields with a value like { max: 5 }.
   * @since 1.12.0
   */
  multi: _propTypes.default.oneOfType([_propTypes.default.bool, _propTypes.default.shape({
    max: _propTypes.default.number.isRequired
  })]),
  /**
   * @ignore Disabled state of multiple field.
   */
  disabled: _propTypes.default.bool,
  /**
   * Localization messages
   * @since 1.12.0
   */
  locale: _propTypes.default.shape({
    addMore: _propTypes.default.node,
    remove: _propTypes.default.node
  }),
  /**
   * Callback which called for all values. If it returns 'true' a pseudo-link 'Add more' will be disabled
   * @ignore
   * @deprecated
   */
  addMoreValidator: _propTypes.default.func,
  /**
   * A callback function, can be executed when the row is removed.
   * @since 1.12.0
   */
  onRemoveRow: _propTypes.default.func,
  /**
   * @ignore
   */
  className: _propTypes.default.string,
  /**
   * @ignore
   */
  baseClassName: _propTypes.default.string,
  /**
   * @ignore
   */
  form: _propTypes.default.object,
  /**
   * Vertical or horizontal field view.
   * If it is not defined explicitly, the vertical mode will use from Form or Section components.
   * @since 3.13.0
   */
  vertical: _propTypes.default.bool,
  /**
   * @ignore
   */
  scrollableElement: _propTypes.default.oneOfType([_propTypes.default.instanceOf(Element), _propTypes.default.instanceOf(Window)])
};
const BASE_FORM_FIELD_CLASS_NAME = exports.BASE_FORM_FIELD_CLASS_NAME = `${_constants.CLS_PREFIX}form-field`;
const defaultProps = {
  id: undefined,
  name: undefined,
  value: defaultValue,
  onChange: null,
  label: undefined,
  description: undefined,
  errors: {},
  required: false,
  children: undefined,
  multi: undefined,
  disabled: undefined,
  locale: undefined,
  addMoreValidator: undefined,
  onRemoveRow: null,
  fullDescription: undefined,
  className: undefined,
  baseClassName: BASE_FORM_FIELD_CLASS_NAME,
  form: undefined,
  vertical: undefined,
  scrollableElement: undefined
};
FormField.propTypes = propTypes;
FormField.defaultProps = defaultProps;

/**
 * `FormField` component is used for showing of parameter values in Form or
 * for entering of user data with any form elements.
 *
 * For the most cases the following form elements can be used:
 * * [FormFieldText](#!/FormFieldText) for entering text,
 * * [FormFieldSelect](#!/FormFieldSelect), [FormFieldCheckbox](#!/FormFieldCheckbox),
 * [FormFieldRadioButtons](#!/FormFieldRadioButtons) for selecting options.
 */

const FormFieldWrapper = props => {
  const form = (0, _react.useContext)(_FormContext.default);
  const verticalContext = (0, _react.useContext)(_VerticalContext.default);
  const vertical = props.vertical === undefined ? verticalContext : props.vertical;
  const scrollableElement = (0, _react.useContext)(_ScrollableElementFormContext.default);
  const disabled = (0, _react.useContext)(_DisabledContext.default);
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(FormField, {
    ...props,
    form: form,
    vertical: vertical,
    scrollableElement: scrollableElement,
    disabled: props.disabled || disabled
  });
};
FormFieldWrapper.displayName = 'FormField';
FormFieldWrapper.propTypes = propTypes;
FormFieldWrapper.defaultProps = defaultProps;
var _default = exports.default = FormFieldWrapper;