import { defineComponent, mergeModels, useModel, useTemplateRef, computed, createElementBlock, openBlock, unref, normalizeClass, renderSlot, createCommentVNode, createElementVNode, mergeProps, normalizeProps, guardReactiveProps, toDisplayString } from "vue";
import { s as stopEvent, e as eventOnOff } from "./event-a_bi5ysw.mjs";
import { C as CODE_UP, a as CODE_DOWN, b as CODE_PAGEUP, c as CODE_PAGEDOWN, d as CODE_HOME, e as CODE_END } from "./constants-CXMQW_qx.mjs";
import { c as useFocus, o as onKeyStroke } from "./index-nMtnPEP0.mjs";
import { u as useDefaults } from "./useDefaults-DvqfKEoD.mjs";
import { u as useId } from "./useId-9_HUwM96.mjs";
import { u as useRtl } from "./useRtl-B6hKZO1m.mjs";
import { u as useToNumber } from "./index-DX8Yoi0C.mjs";
const _hoisted_1 = ["lang", "tabindex", "title"];
const _hoisted_2 = ["name", "form", "value"];
const _hoisted_3 = ["id", "dir", "tabindex", "aria-label", "aria-invalid", "aria-required", "aria-valuemin", "aria-valuemax", "aria-valuenow", "aria-valuetext"];
const defaultValues = {
  min: 1,
  max: 100,
  step: 1,
  repeatDelay: 500,
  repeatInterval: 100,
  repeatThreshold: 10,
  repeatMultiplier: 4
};
const _sfc_main = /* @__PURE__ */ defineComponent({
  __name: "BFormSpinbutton",
  props: /* @__PURE__ */ mergeModels({
    ariaControls: { default: void 0 },
    ariaLabel: { default: void 0 },
    disabled: { type: Boolean, default: false },
    form: { default: void 0 },
    formatterFn: { type: Function, default: void 0 },
    id: { default: void 0 },
    inline: { type: Boolean, default: false },
    labelDecrement: { default: "Decrement" },
    labelIncrement: { default: "Increment" },
    locale: { default: void 0 },
    max: { default: defaultValues.max },
    min: { default: defaultValues.min },
    name: { default: void 0 },
    placeholder: { default: void 0 },
    readonly: { type: Boolean, default: false },
    repeatDelay: { default: defaultValues.repeatDelay },
    repeatInterval: { default: defaultValues.repeatInterval },
    repeatStepMultiplier: { default: defaultValues.repeatMultiplier },
    repeatThreshold: { default: defaultValues.repeatThreshold },
    required: { type: Boolean, default: false },
    size: { default: void 0 },
    state: { type: [Boolean, null], default: null },
    step: { default: defaultValues.step },
    vertical: { type: Boolean, default: false },
    wrap: { type: Boolean, default: false }
  }, {
    "modelValue": {
      default: null
    },
    "modelModifiers": {}
  }),
  emits: /* @__PURE__ */ mergeModels(["change"], ["update:modelValue"]),
  setup(__props, { emit: __emit }) {
    const KEY_CODES = [CODE_UP, CODE_DOWN, CODE_HOME, CODE_END, CODE_PAGEUP, CODE_PAGEDOWN];
    const _props = __props;
    const props = useDefaults(_props, "BFormSpinbutton");
    const emit = __emit;
    const modelValue = useModel(__props, "modelValue");
    const element = useTemplateRef("_element");
    const { focused } = useFocus(element);
    const computedId = useId(() => props.id, "spinbutton");
    const computedClasses = computed(() => ({
      "disabled": props.disabled,
      "readonly": props.readonly,
      "focus": focused.value,
      "d-inline-flex": props.inline || props.vertical,
      "d-flex": !props.inline && !props.vertical,
      "align-items-stretch": !props.vertical,
      "flex-column": props.vertical,
      [`form-control-${props.size}`]: props.size !== void 0
    }));
    const computedSpinClasses = computed(() => ({
      "d-flex": props.vertical,
      "align-self-center": !props.vertical,
      "align-items-center": props.vertical,
      "border-top": props.vertical,
      "border-bottom": props.vertical,
      "border-start": !props.vertical,
      "border-end": !props.vertical
    }));
    let $_autoDelayTimer;
    let $_autoRepeatTimer;
    let $_keyIsDown = false;
    const stepNumber = useToNumber(() => props.step);
    const computedStep = computed(
      () => Number.isNaN(stepNumber.value) ? defaultValues.step : stepNumber.value
    );
    const minNumber = useToNumber(() => props.min);
    const computedMin = computed(
      () => Number.isNaN(minNumber.value) ? defaultValues.min : minNumber.value
    );
    const maxNumber = useToNumber(() => props.max);
    const computedMax = computed(() => {
      const step = computedStep.value;
      const min = computedMin.value;
      return Math.floor((maxNumber.value - min) / step) * step + min;
    });
    const repeatDelayNumber = useToNumber(() => props.repeatDelay, {
      nanToZero: true,
      method: "parseInt"
    });
    const computedDelay = computed(
      () => repeatDelayNumber.value > 0 ? repeatDelayNumber.value : defaultValues.repeatDelay
    );
    const repeatIntervalNumber = useToNumber(() => props.repeatInterval, {
      nanToZero: true,
      method: "parseInt"
    });
    const computedInterval = computed(
      () => repeatIntervalNumber.value > 0 ? repeatIntervalNumber.value : defaultValues.repeatInterval
    );
    const repeatThresholdNumber = useToNumber(() => props.repeatThreshold, {
      nanToZero: true,
      method: "parseInt"
    });
    const computedThreshold = computed(
      () => Math.max(
        Number.isNaN(repeatThresholdNumber.value) ? defaultValues.repeatThreshold : repeatThresholdNumber.value,
        1
      )
    );
    const repeatStepMultiplierNumber = useToNumber(() => props.repeatStepMultiplier, {
      nanToZero: true,
      method: "parseInt"
    });
    const computedStepMultiplier = computed(
      () => Math.max(
        Number.isNaN(repeatStepMultiplierNumber.value) ? defaultValues.repeatMultiplier : repeatStepMultiplierNumber.value,
        1
      )
    );
    const computedPrecision = computed(() => {
      const step = computedStep.value;
      return Math.floor(step) === step ? 0 : (step.toString().split(".")[1] || "").length;
    });
    const computedMultiplier = computed(() => Math.pow(10, computedPrecision.value || 0));
    const valueAsFixed = computed(
      () => modelValue.value === null ? "" : modelValue.value.toFixed(computedPrecision.value)
    );
    const { isRtl, locale: globalLocale } = useRtl();
    const computedLocale = computed(() => {
      const loc = (props.locale ?? globalLocale?.value) || "locale";
      const locales = [loc];
      const nf = new Intl.NumberFormat(locales);
      return nf.resolvedOptions().locale;
    });
    const defaultFormatter = () => new Intl.NumberFormat(computedLocale.value, {
      style: "decimal",
      useGrouping: false,
      minimumIntegerDigits: 1,
      minimumFractionDigits: computedPrecision.value,
      maximumFractionDigits: computedPrecision.value,
      notation: "standard"
    }).format;
    const computedFormatter = computed(() => props.formatterFn ?? defaultFormatter());
    const stepValue = (direction) => {
      let { value } = modelValue;
      if (!props.disabled && value !== null) {
        const step = computedStep.value * direction;
        const min = computedMin.value;
        const max = computedMax.value;
        const multiplier = computedMultiplier.value;
        const { wrap } = props;
        value = Math.round((value - min) / step) * step + min + step;
        value = Math.round(value * multiplier) / multiplier;
        modelValue.value = value > max ? wrap ? min : max : value < min ? wrap ? max : min : value;
      }
    };
    const stepUp = (multiplier = 1) => {
      if (modelValue.value === null) {
        modelValue.value = computedMin.value;
        return;
      }
      stepValue(1 * multiplier);
    };
    const stepDown = (multiplier = 1) => {
      if (modelValue.value === null) {
        modelValue.value = props.wrap ? computedMax.value : computedMin.value;
        return;
      }
      stepValue(-1 * multiplier);
    };
    onKeyStroke(
      KEY_CODES,
      (event) => {
        const { code, altKey, ctrlKey, metaKey } = event;
        if (props.disabled || props.readonly || altKey || ctrlKey || metaKey) return;
        stopEvent(event, { immediatePropagation: true });
        if ($_keyIsDown) {
          return;
        }
        resetTimers();
        if ([CODE_UP, CODE_DOWN].includes(code)) {
          $_keyIsDown = true;
          if (code === CODE_UP) {
            handleStepRepeat(event, stepUp);
            return;
          }
          if (code === CODE_DOWN) {
            handleStepRepeat(event, stepDown);
          }
          return;
        }
        if (code === CODE_PAGEUP) {
          stepUp(computedStepMultiplier.value);
          return;
        }
        if (code === CODE_PAGEDOWN) {
          stepDown(computedStepMultiplier.value);
          return;
        }
        if (code === CODE_HOME) {
          modelValue.value = computedMin.value;
          return;
        }
        if (code === CODE_END) {
          modelValue.value = computedMax.value;
        }
      },
      { target: element, eventName: "keydown" }
    );
    onKeyStroke(
      KEY_CODES,
      (event) => {
        const { altKey, ctrlKey, metaKey } = event;
        if (props.disabled || props.readonly || altKey || ctrlKey || metaKey) return;
        stopEvent(event, { immediatePropagation: true });
        resetTimers();
        $_keyIsDown = false;
        emit("change", modelValue.value);
      },
      { target: element, eventName: "keyup" }
    );
    const handleStepRepeat = (event, stepper) => {
      const { type } = event || {};
      if (!props.disabled && !props.readonly) {
        if (isMouseEvent(event)) {
          if (type === "mousedown" && event.button) return;
        }
        resetTimers();
        stepper(1);
        const threshold = computedThreshold.value;
        const multiplier = computedStepMultiplier.value;
        const delay = computedDelay.value;
        const interval = computedInterval.value;
        $_autoDelayTimer = setTimeout(() => {
          let count = 0;
          $_autoRepeatTimer = setInterval(() => {
            stepper(count < threshold ? 1 : multiplier);
            count++;
          }, interval);
        }, delay);
      }
    };
    const isMouseEvent = (evt) => evt.type === "mouseup" || evt.type === "mousedown";
    const onMouseup = (event) => {
      if (isMouseEvent(event)) {
        if (event.type === "mouseup" && event.button) {
          return;
        }
      }
      stopEvent(event, { immediatePropagation: true });
      resetTimers();
      setMouseup(false);
      emit("change", modelValue.value);
    };
    const setMouseup = (on) => {
      try {
        eventOnOff(on, [document.body, "mouseup", onMouseup, false]);
        eventOnOff(on, [document.body, "touchend", onMouseup, false]);
      } catch {
      }
    };
    const resetTimers = () => {
      clearTimeout($_autoDelayTimer);
      clearInterval($_autoRepeatTimer);
      $_autoDelayTimer = void 0;
      $_autoRepeatTimer = void 0;
    };
    const buttons = computed(() => {
      const incrementSvgAttrs = {
        svg: {
          xmlns: "http://www.w3.org/2000/svg",
          width: "16",
          height: "16",
          fill: "currentColor",
          class: "bi bi-plus",
          viewBox: "0 0 16 16"
        },
        path: {
          d: "M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"
        }
      };
      const decrementSvgAttrs = {
        svg: {
          xmlns: "http://www.w3.org/2000/svg",
          width: "16",
          height: "16",
          fill: "currentColor",
          class: "bi bi-dash",
          viewBox: "0 0 16 16"
        },
        path: { d: "M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8z" }
      };
      const sharedButtonAttrs = {
        "class": [{ "py-0": !props.vertical }, "btn", "btn-sm", "border-0", "rounded-0"],
        "tabindex": "-1",
        "type": "button",
        "disabled": props.disabled || props.readonly,
        "aria-disabled": props.disabled || props.readonly ? true : void 0,
        "aria-controls": computedId.value
      };
      const sharedSvgAttrs = {
        "aria-hidden": true,
        "scale": focused.value ? 1.5 : 1.25
      };
      const handler = (event, stepper) => {
        if (!props.disabled && !props.readonly) {
          stopEvent(event, { immediatePropagation: true });
          setMouseup(true);
          focused.value = true;
          handleStepRepeat(event, stepper);
        }
      };
      const incrementAttrs = {
        button: {
          ...sharedButtonAttrs,
          "aria-label": props.labelIncrement || void 0,
          "aria-keyshortcuts": "ArrowUp"
        },
        svg: {
          ...sharedSvgAttrs,
          ...incrementSvgAttrs.svg
        },
        path: {
          ...incrementSvgAttrs.path
        },
        slot: {
          name: "increment"
        },
        handler: (e) => handler(e, stepUp)
      };
      const decrementAttrs = {
        button: {
          ...sharedButtonAttrs,
          "aria-label": props.labelDecrement || void 0,
          "aria-keyshortcuts": "ArrowDown"
        },
        svg: {
          ...sharedSvgAttrs,
          ...decrementSvgAttrs.svg
        },
        path: {
          ...decrementSvgAttrs.path
        },
        slot: {
          name: "decrement"
        },
        handler: (e) => handler(e, stepDown)
      };
      return {
        top: {
          ...props.vertical ? incrementAttrs : decrementAttrs
        },
        bottom: {
          ...!props.vertical ? incrementAttrs : decrementAttrs
        }
      };
    });
    return (_ctx, _cache) => {
      return openBlock(), createElementBlock("div", {
        ref: "_element",
        class: normalizeClass(["b-form-spinbutton form-control", computedClasses.value]),
        role: "group",
        lang: computedLocale.value,
        tabindex: unref(props).disabled ? void 0 : "-1",
        title: unref(props).ariaLabel,
        onClick: _cache[4] || (_cache[4] = ($event) => focused.value = true)
      }, [
        renderSlot(_ctx.$slots, buttons.value.top.slot.name, { hasFocus: unref(focused) }, () => [
          createElementVNode("button", mergeProps(buttons.value.top.button, {
            onMousedown: _cache[0] || (_cache[0] = //@ts-ignore
            (...args) => buttons.value.top.handler && buttons.value.top.handler(...args)),
            onTouchstart: _cache[1] || (_cache[1] = //@ts-ignore
            (...args) => buttons.value.top.handler && buttons.value.top.handler(...args))
          }), [
            (openBlock(), createElementBlock("svg", normalizeProps(guardReactiveProps(buttons.value.top.svg)), [
              createElementVNode("path", normalizeProps(guardReactiveProps(buttons.value.top.path)), null, 16)
            ], 16))
          ], 16)
        ]),
        unref(props).name && !unref(props).disabled ? (openBlock(), createElementBlock("input", {
          key: "hidden",
          type: "hidden",
          name: unref(props).name,
          form: unref(props).form,
          value: valueAsFixed.value
        }, null, 8, _hoisted_2)) : createCommentVNode("", true),
        createElementVNode("output", {
          id: unref(computedId),
          key: "output",
          class: normalizeClass(["flex-grow-1", computedSpinClasses.value]),
          dir: unref(isRtl) ?? false ? "rtl" : "ltr",
          tabindex: unref(props).disabled ? void 0 : "0",
          role: "spinbutton",
          "aria-live": "off",
          "aria-label": unref(props).ariaLabel || void 0,
          "aria-invalid": unref(props).state === false || !modelValue.value !== null && unref(props).required ? true : void 0,
          "aria-required": unref(props).required ? true : void 0,
          "aria-valuemin": computedMin.value,
          "aria-valuemax": computedMax.value,
          "aria-valuenow": modelValue.value !== null ? modelValue.value : void 0,
          "aria-valuetext": modelValue.value !== null ? computedFormatter.value(modelValue.value) : void 0
        }, [
          createElementVNode("bdi", null, toDisplayString((modelValue.value !== null ? computedFormatter.value(modelValue.value) : unref(props).placeholder) || ""), 1)
        ], 10, _hoisted_3),
        renderSlot(_ctx.$slots, buttons.value.bottom.slot.name, { hasFocus: unref(focused) }, () => [
          createElementVNode("button", mergeProps(buttons.value.bottom.button, {
            onMousedown: _cache[2] || (_cache[2] = //@ts-ignore
            (...args) => buttons.value.bottom.handler && buttons.value.bottom.handler(...args)),
            onTouchstart: _cache[3] || (_cache[3] = //@ts-ignore
            (...args) => buttons.value.bottom.handler && buttons.value.bottom.handler(...args))
          }), [
            (openBlock(), createElementBlock("svg", normalizeProps(guardReactiveProps(buttons.value.bottom.svg)), [
              createElementVNode("path", normalizeProps(guardReactiveProps(buttons.value.bottom.path)), null, 16)
            ], 16))
          ], 16)
        ])
      ], 10, _hoisted_1);
    };
  }
});
export {
  _sfc_main as _
};
//# sourceMappingURL=BFormSpinbutton.vue_vue_type_script_setup_true_lang-CRVWCMyj.mjs.map
