(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('imask'), require('react'), require('prop-types')) :
  typeof define === 'function' && define.amd ? define(['exports', 'imask', 'react', 'prop-types'], factory) :
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ReactIMask = {}, global.IMask, global.React, global.PropTypes));
})(this, (function (exports, IMask, React, PropTypes) { 'use strict';

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

  var IMask__default = /*#__PURE__*/_interopDefault(IMask);
  var React__default = /*#__PURE__*/_interopDefault(React);
  var PropTypes__default = /*#__PURE__*/_interopDefault(PropTypes);

  const MASK_PROPS = {
    // common
    mask: PropTypes__default.default.oneOfType([PropTypes__default.default.array, PropTypes__default.default.func, PropTypes__default.default.string, PropTypes__default.default.instanceOf(RegExp), PropTypes__default.default.oneOf([Date, Number, IMask__default.default.Masked]), PropTypes__default.default.instanceOf(IMask__default.default.Masked)]),
    value: PropTypes__default.default.any,
    unmask: PropTypes__default.default.oneOfType([PropTypes__default.default.bool, PropTypes__default.default.oneOf(['typed'])]),
    prepare: PropTypes__default.default.func,
    prepareChar: PropTypes__default.default.func,
    validate: PropTypes__default.default.func,
    commit: PropTypes__default.default.func,
    overwrite: PropTypes__default.default.oneOfType([PropTypes__default.default.bool, PropTypes__default.default.oneOf(['shift'])]),
    eager: PropTypes__default.default.oneOfType([PropTypes__default.default.bool, PropTypes__default.default.oneOf(['append', 'remove'])]),
    skipInvalid: PropTypes__default.default.bool,
    // events
    onAccept: PropTypes__default.default.func,
    onComplete: PropTypes__default.default.func,
    // pattern
    placeholderChar: PropTypes__default.default.string,
    displayChar: PropTypes__default.default.string,
    lazy: PropTypes__default.default.bool,
    definitions: PropTypes__default.default.object,
    blocks: PropTypes__default.default.object,
    // enum
    enum: PropTypes__default.default.arrayOf(PropTypes__default.default.string),
    // range
    maxLength: PropTypes__default.default.number,
    from: PropTypes__default.default.number,
    to: PropTypes__default.default.number,
    // date
    pattern: PropTypes__default.default.string,
    format: PropTypes__default.default.func,
    parse: PropTypes__default.default.func,
    autofix: PropTypes__default.default.oneOfType([PropTypes__default.default.bool, PropTypes__default.default.oneOf(['pad'])]),
    // number
    radix: PropTypes__default.default.string,
    thousandsSeparator: PropTypes__default.default.string,
    mapToRadix: PropTypes__default.default.arrayOf(PropTypes__default.default.string),
    scale: PropTypes__default.default.number,
    normalizeZeros: PropTypes__default.default.bool,
    padFractionalZeros: PropTypes__default.default.bool,
    min: PropTypes__default.default.oneOfType([PropTypes__default.default.number, PropTypes__default.default.instanceOf(Date)]),
    max: PropTypes__default.default.oneOfType([PropTypes__default.default.number, PropTypes__default.default.instanceOf(Date)]),
    // dynamic
    dispatch: PropTypes__default.default.func,
    // ref
    inputRef: PropTypes__default.default.oneOfType([PropTypes__default.default.func, PropTypes__default.default.shape({
      current: PropTypes__default.default.object
    })])
  };
  const MASK_PROPS_NAMES = Object.keys(MASK_PROPS).filter(p => p !== 'value');
  const NON_MASK_OPTIONS_PROPS_NAMES = ['value', 'unmask', 'onAccept', 'onComplete', 'inputRef'];
  const MASK_OPTIONS_PROPS_NAMES = MASK_PROPS_NAMES.filter(pName => NON_MASK_OPTIONS_PROPS_NAMES.indexOf(pName) < 0);
  function IMaskMixin(ComposedComponent) {
    var _class;
    const MaskedComponent = (_class = class MaskedComponent extends React__default.default.Component {
      constructor(props) {
        super(props);
        this._inputRef = this._inputRef.bind(this);
      }
      componentDidMount() {
        if (!this.props.mask) return;
        this.initMask();
      }
      componentDidUpdate() {
        const props = this.props;
        const maskOptions = this._extractMaskOptionsFromProps(props);
        if (maskOptions.mask) {
          if (this.maskRef) {
            this.maskRef.updateOptions(maskOptions); // TODO fix
            if ('value' in props && props.value !== undefined) this.maskValue = props.value;
          } else {
            this.initMask(maskOptions);
          }
        } else {
          this.destroyMask();
          if ('value' in props && props.value !== undefined) {
            var _this$element;
            if ((_this$element = this.element) != null && _this$element.isContentEditable && this.element.tagName !== 'INPUT' && this.element.tagName !== 'TEXTAREA') this.element.textContent = props.value;else this.element.value = props.value;
          }
        }
      }
      componentWillUnmount() {
        this.destroyMask();
      }
      _inputRef(el) {
        this.element = el;
        if (this.props.inputRef) {
          if (Object.prototype.hasOwnProperty.call(this.props.inputRef, 'current')) this.props.inputRef.current = el;else this.props.inputRef(el);
        }
      }
      initMask(maskOptions) {
        if (maskOptions === void 0) {
          maskOptions = this._extractMaskOptionsFromProps(this.props);
        }
        this.maskRef = IMask__default.default(this.element, maskOptions).on('accept', this._onAccept.bind(this)).on('complete', this._onComplete.bind(this));
        if ('value' in this.props && this.props.value !== undefined) this.maskValue = this.props.value;
      }
      destroyMask() {
        if (this.maskRef) {
          this.maskRef.destroy();
          delete this.maskRef;
        }
      }
      _extractMaskOptionsFromProps(props) {
        const {
          ...cloneProps
        } = props;

        // keep only mask options
        Object.keys(cloneProps).filter(prop => MASK_OPTIONS_PROPS_NAMES.indexOf(prop) < 0).forEach(nonMaskProp => {
          delete cloneProps[nonMaskProp];
        });
        return cloneProps;
      }
      _extractNonMaskProps(props) {
        const {
          ...cloneProps
        } = props;
        MASK_PROPS_NAMES.forEach(maskProp => {
          delete cloneProps[maskProp];
        });
        if (!('defaultValue' in cloneProps)) cloneProps.defaultValue = props.mask ? '' : cloneProps.value;
        delete cloneProps.value;
        return cloneProps;
      }
      get maskValue() {
        if (!this.maskRef) return '';
        if (this.props.unmask === 'typed') return this.maskRef.typedValue;
        if (this.props.unmask) return this.maskRef.unmaskedValue;
        return this.maskRef.value;
      }
      set maskValue(value) {
        if (!this.maskRef) return;
        value = value == null && this.props.unmask !== 'typed' ? '' : value;
        if (this.props.unmask === 'typed') this.maskRef.typedValue = value;else if (this.props.unmask) this.maskRef.unmaskedValue = value;else this.maskRef.value = value;
      }
      _onAccept(e) {
        if (this.props.onAccept && this.maskRef) this.props.onAccept(this.maskValue, this.maskRef, e);
      }
      _onComplete(e) {
        if (this.props.onComplete && this.maskRef) this.props.onComplete(this.maskValue, this.maskRef, e);
      }
      render() {
        return React__default.default.createElement(ComposedComponent, {
          ...this._extractNonMaskProps(this.props),
          inputRef: this._inputRef
        });
      }
    }, _class.displayName = void 0, _class.propTypes = void 0, _class);
    const nestedComponentName = ComposedComponent.displayName || ComposedComponent.name || 'Component';
    MaskedComponent.displayName = "IMask(" + nestedComponentName + ")";
    MaskedComponent.propTypes = MASK_PROPS;
    return React__default.default.forwardRef((props, ref) => React__default.default.createElement(MaskedComponent, {
      ...props,
      ref
    }));
  }

  const IMaskInputClass = IMaskMixin(_ref => {
    let {
      inputRef,
      ...props
    } = _ref;
    return React__default.default.createElement('input', {
      ...props,
      ref: inputRef
    });
  });
  const IMaskInputFn = (props, ref) => React__default.default.createElement(IMaskInputClass, {
    ...props,
    ref
  }) // TODO fix no idea
  ;

  const IMaskInput = React__default.default.forwardRef(IMaskInputFn);

  function useIMask(opts, _temp) {
    let {
      onAccept,
      onComplete
    } = _temp === void 0 ? {} : _temp;
    const ref = React.useRef(null);
    const maskRef = React.useRef(null);
    const [initialized, setInitialized] = React.useState(false);
    const [lastAcceptState, setLastAcceptState] = React.useState({});
    const [value, setValue] = React.useState('');
    const [unmaskedValue, setUnmaskedValue] = React.useState('');
    const [typedValue, setTypedValue] = React.useState();
    const _destroyMask = React.useCallback(() => {
      var _maskRef$current;
      if (!initialized) return;
      (_maskRef$current = maskRef.current) == null ? void 0 : _maskRef$current.destroy();
      maskRef.current = null;
    }, []);
    const _onAccept = React.useCallback(event => {
      const m = maskRef.current;
      if (!m) return;
      setLastAcceptState({
        value: m.value,
        unmaskedValue: m.unmaskedValue,
        typedValue: m.typedValue
      });
      setTypedValue(m.typedValue);
      setUnmaskedValue(m.unmaskedValue);
      setValue(m.value);
      onAccept == null ? void 0 : onAccept(m.value, m, event);
    }, [onAccept]);
    const _onComplete = React.useCallback(() => maskRef.current && (onComplete == null ? void 0 : onComplete(maskRef.current.value, maskRef.current)), [onComplete]);
    React.useEffect(() => {
      const el = ref.current;
      if (!el || !(opts != null && opts.mask)) return _destroyMask();
      const mask = maskRef.current;
      if (!mask) {
        if (el && opts != null && opts.mask) {
          maskRef.current = IMask__default.default(el, opts);
          _onAccept();
        }
      } else {
        mask == null ? void 0 : mask.updateOptions(opts); // TODO fix no idea
      }

      setInitialized(Boolean(maskRef.current));
    }, [opts, _destroyMask, _onAccept]);
    React.useEffect(() => {
      if (!maskRef.current) return;
      const mask = maskRef.current;
      mask.on('accept', _onAccept);
      mask.on('complete', _onComplete);
      return () => {
        mask.off('accept', _onAccept);
        mask.off('complete', _onComplete);
      };
    }, [_onAccept, _onComplete]);
    React.useEffect(() => {
      const {
        value: lastAcceptValue,
        ...state
      } = lastAcceptState;
      const mask = maskRef.current;
      if (mask && initialized) {
        if (lastAcceptValue !== value) mask.value = value;
        setLastAcceptState(state);
      }
    }, [value]);
    React.useEffect(() => {
      const {
        unmaskedValue: lastAcceptUnmaskedValue,
        ...state
      } = lastAcceptState;
      const mask = maskRef.current;
      if (mask && initialized) {
        if (lastAcceptUnmaskedValue !== unmaskedValue) mask.unmaskedValue = unmaskedValue;
        setLastAcceptState(state);
      }
    }, [unmaskedValue]);
    React.useEffect(() => {
      const {
        typedValue: lastAcceptTypedValue,
        ...state
      } = lastAcceptState;
      const mask = maskRef.current;
      if (mask && initialized) {
        if (lastAcceptTypedValue !== typedValue) mask.typedValue = typedValue;
        setLastAcceptState(state);
      }
    }, [typedValue]);
    React.useEffect(() => _destroyMask, [_destroyMask]);
    return {
      ref,
      maskRef,
      value,
      setValue,
      unmaskedValue,
      setUnmaskedValue,
      typedValue,
      setTypedValue
    };
  }

  Object.defineProperty(exports, 'IMask', {
    enumerable: true,
    get: function () { return IMask__default.default; }
  });
  exports.IMaskInput = IMaskInput;
  exports.IMaskMixin = IMaskMixin;
  exports.useIMask = useIMask;

}));
//# sourceMappingURL=react-imask.js.map
