'use strict';

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

var vue = require('vue');
var type = require('../../_util/type.js');
var classNames = require('../../_util/classNames.js');
var KeyCode = require('../../_util/KeyCode.js');
var useFrame = require('./hooks/useFrame.js');
var useCursor = require('./hooks/useCursor.js');
var numberUtil = require('./utils/numberUtil.js');
var StepHandler = require('./StepHandler.js');
var MiniDecimal = require('./utils/MiniDecimal.js');

const getDecimalValue = (stringMode, decimalValue) => {
  if (stringMode || decimalValue.isEmpty())
    return decimalValue.toString();
  return decimalValue.toNumber();
};
const getDecimalIfValidate = (value) => {
  const decimal = MiniDecimal.default(value);
  return decimal.isInvalidate() ? null : decimal;
};
const inputNumberProps = () => ({
  /** value will show as string */
  stringMode: type.booleanType(),
  defaultValue: type.someType([String, Number]),
  value: type.someType([String, Number]),
  prefixCls: type.stringType(),
  min: type.someType([String, Number]),
  max: type.someType([String, Number]),
  step: type.someType([String, Number], 1),
  tabindex: Number,
  controls: type.booleanType(true),
  readonly: type.booleanType(),
  disabled: type.booleanType(),
  autofocus: type.booleanType(),
  keyboard: type.booleanType(true),
  /** Parse display value to validate number */
  parser: type.functionType(),
  /** Transform `value` to display value show in input */
  formatter: type.functionType(),
  /** Syntactic sugar of `formatter`. Config precision of display. */
  precision: Number,
  /** Syntactic sugar of `formatter`. Config decimal separator of display. */
  decimalSeparator: String,
  onInput: type.functionType(),
  onChange: type.functionType(),
  onPressEnter: type.functionType(),
  onStep: type.functionType(),
  onBlur: type.functionType(),
  onFocus: type.functionType()
});
var VcInputNumber = /* @__PURE__ */ vue.defineComponent({
  compatConfig: {
    MODE: 3
  },
  name: "InnerInputNumber",
  inheritAttrs: false,
  props: {
    ...inputNumberProps(),
    lazy: Boolean
  },
  slots: Object,
  setup(props, {
    attrs,
    slots,
    emit,
    expose
  }) {
    const inputRef = vue.shallowRef();
    const focus = vue.shallowRef(false);
    const userTypingRef = vue.shallowRef(false);
    const compositionRef = vue.shallowRef(false);
    const decimalValue = vue.shallowRef(MiniDecimal.default(props.value));
    function setUncontrolledDecimalValue(newDecimal) {
      if (props.value === void 0)
        decimalValue.value = newDecimal;
    }
    const getPrecision = (numStr, userTyping) => {
      if (userTyping)
        return void 0;
      if (props.precision >= 0)
        return props.precision;
      return Math.max(numberUtil.getNumberPrecision(numStr), numberUtil.getNumberPrecision(props.step));
    };
    const mergedParser = (num) => {
      const numStr = String(num);
      if (props.parser)
        return props.parser(numStr);
      let parsedStr = numStr;
      if (props.decimalSeparator)
        parsedStr = parsedStr.replace(props.decimalSeparator, ".");
      return parsedStr.replace(/[^\w.-]+/g, "");
    };
    const inputValue = vue.shallowRef("");
    const mergedFormatter = (number, userTyping) => {
      if (props.formatter)
        return props.formatter(number, {
          userTyping,
          input: String(inputValue.value)
        });
      let str = typeof number === "number" ? numberUtil.num2str(number) : number;
      if (!userTyping) {
        const mergedPrecision = getPrecision(str, userTyping);
        if (numberUtil.validateNumber(str) && (props.decimalSeparator || mergedPrecision >= 0)) {
          const separatorStr = props.decimalSeparator || ".";
          str = MiniDecimal.toFixed(str, separatorStr, mergedPrecision);
        }
      }
      return str;
    };
    const initValue = (() => {
      const initValue2 = props.value;
      if (decimalValue.value.isInvalidate() && ["string", "number"].includes(typeof initValue2))
        return Number.isNaN(initValue2) ? "" : initValue2;
      return mergedFormatter(decimalValue.value.toString(), false);
    })();
    inputValue.value = initValue;
    function setInputValue(newValue, userTyping) {
      inputValue.value = mergedFormatter(
        // Invalidate number is sometime passed by external control, we should let it go
        // Otherwise is controlled by internal interactive logic which check by userTyping
        // You can ref 'show limited value when input is not focused' test for more info.
        newValue.isInvalidate() ? newValue.toString(false) : newValue.toString(!userTyping),
        userTyping
      );
    }
    const maxDecimal = vue.computed(() => getDecimalIfValidate(props.max));
    const minDecimal = vue.computed(() => getDecimalIfValidate(props.min));
    const upDisabled = vue.computed(() => {
      if (!maxDecimal.value || !decimalValue.value || decimalValue.value.isInvalidate())
        return false;
      return maxDecimal.value.lessEquals(decimalValue.value);
    });
    const downDisabled = vue.computed(() => {
      if (!minDecimal.value || !decimalValue.value || decimalValue.value.isInvalidate())
        return false;
      return decimalValue.value.lessEquals(minDecimal.value);
    });
    const [recordCursor, restoreCursor] = useCursor.default(inputRef, focus);
    const getRangeValue = (target) => {
      if (maxDecimal.value && !target.lessEquals(maxDecimal.value))
        return maxDecimal.value;
      if (minDecimal.value && !minDecimal.value.lessEquals(target))
        return minDecimal.value;
      return null;
    };
    const isInRange = (target) => !getRangeValue(target);
    const triggerValueUpdate = (newValue, userTyping) => {
      var _a;
      let updateValue = newValue;
      let isRangeValidate = isInRange(updateValue) || updateValue.isEmpty();
      if (!updateValue.isEmpty() && !userTyping) {
        updateValue = getRangeValue(updateValue) || updateValue;
        isRangeValidate = true;
      }
      if (!props.readonly && !props.disabled && isRangeValidate) {
        const numStr = updateValue.toString();
        const mergedPrecision = getPrecision(numStr, userTyping);
        if (mergedPrecision >= 0)
          updateValue = MiniDecimal.default(MiniDecimal.toFixed(numStr, ".", mergedPrecision));
        if (!updateValue.equals(decimalValue.value)) {
          setUncontrolledDecimalValue(updateValue);
          (_a = props.onChange) == null ? void 0 : _a.call(props, updateValue.isEmpty() ? null : getDecimalValue(props.stringMode, updateValue));
          if (props.value === void 0)
            setInputValue(updateValue, userTyping);
        }
        return updateValue;
      }
      return decimalValue.value;
    };
    const onNextPromise = useFrame.default();
    const collectInputValue = (inputStr) => {
      var _a;
      recordCursor();
      inputValue.value = inputStr;
      if (!compositionRef.value) {
        const finalValue = mergedParser(inputStr);
        const finalDecimal = MiniDecimal.default(finalValue);
        if (!finalDecimal.isNaN())
          triggerValueUpdate(finalDecimal, true);
      }
      (_a = props.onInput) == null ? void 0 : _a.call(props, inputStr);
      onNextPromise(() => {
        let nextInputStr = inputStr;
        if (!props.parser)
          nextInputStr = inputStr.replace(/。/g, ".");
        if (nextInputStr !== inputStr)
          collectInputValue(nextInputStr);
      });
    };
    const onCompositionStart = () => {
      compositionRef.value = true;
    };
    const onCompositionEnd = () => {
      compositionRef.value = false;
      collectInputValue(inputRef.value.value);
    };
    const onInternalInput = (e) => {
      collectInputValue(e.target.value);
    };
    const onInternalStep = (up) => {
      var _a, _b;
      if (up && upDisabled.value || !up && downDisabled.value)
        return;
      userTypingRef.value = false;
      let stepDecimal = MiniDecimal.default(props.step);
      if (!up)
        stepDecimal = stepDecimal.negate();
      const target = (decimalValue.value || MiniDecimal.default(0)).add(stepDecimal.toString());
      const updatedValue = triggerValueUpdate(target, false);
      (_a = props.onStep) == null ? void 0 : _a.call(props, getDecimalValue(props.stringMode, updatedValue), {
        offset: props.step,
        type: up ? "up" : "down"
      });
      (_b = inputRef.value) == null ? void 0 : _b.focus();
    };
    const flushInputValue = (userTyping) => {
      const parsedValue = MiniDecimal.default(mergedParser(inputValue.value));
      let formatValue = parsedValue;
      if (!parsedValue.isNaN()) {
        formatValue = triggerValueUpdate(parsedValue, userTyping);
      } else {
        formatValue = decimalValue.value;
      }
      if (props.value !== void 0) {
        setInputValue(decimalValue.value, false);
      } else if (!formatValue.isNaN()) {
        setInputValue(formatValue, false);
      }
    };
    const onKeyDown = (event) => {
      var _a;
      const {
        which
      } = event;
      userTypingRef.value = true;
      if (which === KeyCode.default.ENTER) {
        if (!compositionRef.value)
          userTypingRef.value = false;
        flushInputValue(false);
        (_a = props.onPressEnter) == null ? void 0 : _a.call(props, event);
      }
      if (props.keyboard === false)
        return;
      if (!compositionRef.value && [KeyCode.default.UP, KeyCode.default.DOWN].includes(which)) {
        onInternalStep(KeyCode.default.UP === which);
        event.preventDefault();
      }
    };
    const onKeyUp = () => {
      userTypingRef.value = false;
    };
    const onBlur = (e) => {
      flushInputValue(false);
      focus.value = false;
      userTypingRef.value = false;
      emit("blur", e);
    };
    vue.watch(() => props.precision, () => {
      if (!decimalValue.value.isInvalidate())
        setInputValue(decimalValue.value, false);
    }, {
      flush: "post"
    });
    vue.watch(() => props.value, () => {
      const newValue = MiniDecimal.default(props.value);
      decimalValue.value = newValue;
      const currentParsedValue = MiniDecimal.default(mergedParser(inputValue.value));
      if (!newValue.equals(currentParsedValue) || !userTypingRef.value || props.formatter) {
        setInputValue(newValue, userTypingRef.value);
      }
    }, {
      flush: "post"
    });
    vue.watch(inputValue, () => {
      if (props.formatter)
        restoreCursor();
    }, {
      flush: "post"
    });
    vue.watch(() => props.disabled, (val) => {
      if (val)
        focus.value = false;
    });
    expose({
      focus: () => {
        var _a;
        (_a = inputRef.value) == null ? void 0 : _a.focus();
      },
      blur: () => {
        var _a;
        (_a = inputRef.value) == null ? void 0 : _a.blur();
      }
    });
    return () => {
      const {
        prefixCls = "rc-input-number",
        min,
        max,
        step = 1,
        defaultValue,
        value,
        disabled,
        readonly,
        keyboard,
        controls = true,
        autofocus,
        stringMode,
        parser,
        formatter,
        precision,
        decimalSeparator,
        onChange,
        onInput,
        onPressEnter,
        onStep,
        lazy,
        class: className,
        style,
        ...inputProps
      } = {
        ...attrs,
        ...props
      };
      const {
        upHandler,
        downHandler
      } = slots;
      const inputClassName = `${prefixCls}-input`;
      const eventProps = {};
      if (lazy)
        eventProps.onChange = onInternalInput;
      else
        eventProps.onInput = onInternalInput;
      return vue.createVNode("div", {
        "class": classNames.default(prefixCls, className, {
          [`${prefixCls}-focused`]: focus.value,
          [`${prefixCls}-disabled`]: disabled,
          [`${prefixCls}-readonly`]: readonly,
          [`${prefixCls}-not-a-number`]: decimalValue.value.isNaN(),
          [`${prefixCls}-out-of-range`]: !decimalValue.value.isInvalidate() && !isInRange(decimalValue.value)
        }),
        "style": style,
        "onKeydown": onKeyDown,
        "onKeyup": onKeyUp
      }, [controls && vue.createVNode(StepHandler.default, {
        "prefixCls": prefixCls,
        "upDisabled": upDisabled.value,
        "downDisabled": downDisabled.value,
        "onStep": onInternalStep
      }, {
        upNode: upHandler,
        downNode: downHandler
      }), vue.createVNode("div", {
        "class": `${inputClassName}-wrap`
      }, [vue.createVNode("input", vue.mergeProps({
        "autofocus": autofocus,
        "autocomplete": "off",
        "role": "spinbutton",
        "aria-valuemin": min,
        "aria-valuemax": max,
        "aria-valuenow": decimalValue.value.isInvalidate() ? null : decimalValue.value.toString(),
        "step": step
      }, inputProps, {
        "ref": inputRef,
        "class": inputClassName,
        "value": inputValue.value,
        "disabled": disabled,
        "readonly": readonly,
        "onFocus": (e) => {
          focus.value = true;
          emit("focus", e);
        }
      }, eventProps, {
        "onBlur": onBlur,
        "onCompositionstart": onCompositionStart,
        "onCompositionend": onCompositionEnd
      }), null)])]);
    };
  }
});

exports.default = VcInputNumber;
exports.inputNumberProps = inputNumberProps;
//# sourceMappingURL=InputNumber.js.map
