@mgcodeur/vue-number-input
Version:
A customizable Vue 3 number input component with increment/decrement buttons and long-press adjustment.
164 lines (163 loc) • 5.37 kB
JavaScript
import { defineComponent as M, createElementBlock as v, openBlock as r, createElementVNode as i, ref as b, computed as I, watch as x, withDirectives as j, normalizeStyle as g, createBlock as $, renderSlot as z, vModelText as B } from "vue";
const S = ["width", "height", "stroke-width"], A = /* @__PURE__ */ M({
__name: "MinusIcon",
props: {
size: { default: 24 },
color: { default: "currentColor" },
strokeWidth: { default: 2 }
},
setup(h) {
return (u, n) => (r(), v("svg", {
xmlns: "http://www.w3.org/2000/svg",
width: u.size,
height: u.size,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": u.strokeWidth,
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "icon icon-tabler icons-tabler-outline icon-tabler-minus"
}, n[0] || (n[0] = [
i("path", {
stroke: "none",
d: "M0 0h24v24H0z",
fill: "none"
}, null, -1),
i("path", { d: "M5 12l14 0" }, null, -1)
]), 8, S));
}
}), C = ["width", "height", "stroke-width"], T = /* @__PURE__ */ M({
__name: "PlusIcon",
props: {
size: { default: 24 },
color: { default: "currentColor" },
strokeWidth: { default: 2 }
},
setup(h) {
return (u, n) => (r(), v("svg", {
xmlns: "http://www.w3.org/2000/svg",
width: u.size,
height: u.size,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": u.strokeWidth,
"stroke-linecap": "round",
"stroke-linejoin": "round",
class: "icon icon-tabler icons-tabler-outline icon-tabler-plus"
}, n[0] || (n[0] = [
i("path", {
stroke: "none",
d: "M0 0h24v24H0z",
fill: "none"
}, null, -1),
i("path", { d: "M12 5l0 14" }, null, -1),
i("path", { d: "M5 12l14 0" }, null, -1)
]), 8, C));
}
}), P = { class: "v-number-input" }, W = { key: 0 }, E = ["step", "min", "max", "placeholder"], F = { key: 0 }, D = /* @__PURE__ */ M({
__name: "NumberInput",
props: {
adjustmentSpeed: { default: 100 },
inputPosition: { default: "center" },
max: { default: 1 / 0 },
min: { default: -1 / 0 },
modelValue: { default: 0 },
placeholder: { default: "0" },
step: { default: 1 },
textAlign: { default: "center" }
},
emits: ["update:modelValue"],
setup(h, { emit: u }) {
const n = h, w = u, a = (e) => Math.min(n.max, Math.max(n.min, e)), s = (e) => Number(Number(e).toFixed(y())), c = (e) => s(e).toFixed(y()), y = () => {
var e;
return ((e = n.step.toString().split(".")[1]) == null ? void 0 : e.length) || 0;
}, o = b(c(s(a(n.modelValue)))), p = b(null), l = b(s(a(n.modelValue))), k = I(
() => ({
center: { input: 1, minus: 0, plus: 2 },
left: { input: 0, minus: 1, plus: 2 },
right: { input: 2, minus: 0, plus: 1 }
})[n.inputPosition]
);
function V(e) {
const t = s(a((l.value ?? 0) + (e === "increment" ? n.step : -n.step)));
l.value = t, o.value = c(t), w("update:modelValue", t);
}
function N() {
const e = Number(o.value);
o.value === "" || isNaN(e) ? l.value = s(Math.max(n.min, 0)) : l.value = s(a(e)), o.value = c(l.value), w("update:modelValue", l.value);
}
function _(e) {
o.value = e.target.value;
const t = Number(o.value);
o.value !== "" && !isNaN(t) && (l.value = s(a(t)), w("update:modelValue", l.value));
}
function f(e) {
p.value || (V(e), p.value = window.setInterval(() => V(e), n.adjustmentSpeed));
}
function d() {
p.value && clearInterval(p.value), p.value = null;
}
return x(
() => n.modelValue,
(e) => {
const t = s(a(e));
t !== l.value && (l.value = t, o.value = c(t));
},
{ immediate: !0 }
), (e, t) => (r(), v("div", P, [
i("button", {
class: "v-number-input__button",
onMousedown: t[0] || (t[0] = (m) => f("decrement")),
onMouseup: d,
onMouseleave: d,
onTouchstart: t[1] || (t[1] = (m) => f("decrement")),
onTouchend: d,
style: g({ order: k.value.minus })
}, [
e.$slots["minus-icon"] ? (r(), v("span", W, [
z(e.$slots, "minus-icon")
])) : (r(), $(A, {
key: 1,
size: 12,
"stroke-width": 1.5
}))
], 36),
j(i("input", {
type: "number",
"onUpdate:modelValue": t[2] || (t[2] = (m) => o.value = m),
step: e.step,
min: e.min,
max: e.max,
placeholder: e.placeholder,
class: "v-number-input__input",
onInput: _,
onBlur: N,
style: g({ textAlign: e.textAlign, order: k.value.input }),
lang: "en"
}, null, 44, E), [
[B, o.value]
]),
i("button", {
class: "v-number-input__button",
onMousedown: t[3] || (t[3] = (m) => f("increment")),
onMouseup: d,
onMouseleave: d,
onTouchstart: t[4] || (t[4] = (m) => f("increment")),
onTouchend: d,
style: g({ order: k.value.plus })
}, [
e.$slots["plus-icon"] ? (r(), v("span", F, [
z(e.$slots, "plus-icon")
])) : (r(), $(T, {
key: 1,
size: 12
}))
], 36)
]));
}
});
export {
D as VueNumberInput
};