@chenfengyuan/vue-number-input
Version:
Number input component for Vue 3.
247 lines (235 loc) • 11.1 kB
JavaScript
/*! vue-number-input v2.0.1 | (c) 2018-present Chen Fengyuan | MIT */
import { defineComponent, openBlock, createElementBlock, normalizeClass, createCommentVNode, createElementVNode, mergeProps } from 'vue';
const isNaN$1 = Number.isNaN || window.isNaN;
const REGEXP_NUMBER = /^-?(?:\d+|\d+\.\d+|\.\d+)(?:[eE][-+]?\d+)?$/;
const REGEXP_DECIMALS = /\.\d*(?:0|9){10}\d*$/;
const normalizeDecimalNumber = (value, times = 100000000000) => (REGEXP_DECIMALS.test(String(value)) ? (Math.round(value * times) / times) : value);
var script = defineComponent({
name: 'VueNumberInput',
props: {
attrs: {
type: Object,
default: undefined,
},
center: Boolean,
controls: Boolean,
disabled: Boolean,
inputtable: {
type: Boolean,
default: true,
},
inline: Boolean,
max: {
type: Number,
default: Infinity,
},
min: {
type: Number,
default: -Infinity,
},
name: {
type: String,
default: undefined,
},
placeholder: {
type: String,
default: undefined,
},
readonly: Boolean,
rounded: Boolean,
size: {
type: String,
default: undefined,
},
step: {
type: Number,
default: 1,
},
modelValue: {
type: Number,
default: NaN,
},
},
emits: [
'update:modelValue',
],
data() {
return {
value: NaN,
};
},
computed: {
/**
* Indicate if the value is increasable.
* @returns {boolean} Return `true` if it is decreasable, else `false`.
*/
increasable() {
return isNaN$1(this.value) || this.value < this.max;
},
/**
* Indicate if the value is decreasable.
* @returns {boolean} Return `true` if it is decreasable, else `false`.
*/
decreasable() {
return isNaN$1(this.value) || this.value > this.min;
},
},
watch: {
modelValue: {
immediate: true,
handler(newValue, oldValue) {
if (
// Avoid triggering change event when created
!(isNaN$1(newValue) && typeof oldValue === 'undefined')
// Avoid infinite loop
&& newValue !== this.value) {
this.setValue(newValue);
}
},
},
},
methods: {
isNaN: isNaN$1,
/**
* Change event handler.
* @param {string} value - The new value.
*/
change(event) {
this.setValue(event.target.value);
},
/**
* Paste event handler.
* @param {Event} event - Event object.
*/
paste(event) {
const clipboardData = event.clipboardData || window.clipboardData;
if (clipboardData && !REGEXP_NUMBER.test(clipboardData.getData('text'))) {
event.preventDefault();
}
},
/**
* Decrease the value.
*/
decrease() {
if (this.decreasable) {
let { value } = this;
if (isNaN$1(value)) {
value = 0;
}
this.setValue(normalizeDecimalNumber(value - this.step));
}
},
/**
* Increase the value.
*/
increase() {
if (this.increasable) {
let { value } = this;
if (isNaN$1(value)) {
value = 0;
}
this.setValue(normalizeDecimalNumber(value + this.step));
}
},
/**
* Set new value and dispatch change event.
* @param {number} value - The new value to set.
*/
setValue(value) {
const oldValue = this.value;
let newValue = typeof value !== 'number' ? parseFloat(value) : value;
if (!isNaN$1(newValue)) {
if (this.min <= this.max) {
newValue = Math.min(this.max, Math.max(this.min, newValue));
}
if (this.rounded) {
newValue = Math.round(newValue);
}
}
this.value = newValue;
if (newValue === oldValue) {
// Force to override the number in the input box (#13).
this.$refs.input.value = String(newValue);
}
this.$emit('update:modelValue', newValue, oldValue);
},
},
});
const _hoisted_1 = ["disabled"];
const _hoisted_2 = ["name", "value", "min", "max", "step", "readonly", "disabled", "placeholder"];
const _hoisted_3 = ["disabled"];
function render(_ctx, _cache, $props, $setup, $data, $options) {
return (openBlock(), createElementBlock("div", {
class: normalizeClass(["vue-number-input", {
'vue-number-input--inline': _ctx.inline,
'vue-number-input--center': _ctx.center,
'vue-number-input--controls': _ctx.controls,
[`vue-number-input--${_ctx.size}`]: _ctx.size,
}])
}, [
(_ctx.controls)
? (openBlock(), createElementBlock("button", {
key: 0,
class: "vue-number-input__button vue-number-input__button--minus",
type: "button",
tabindex: "-1",
disabled: _ctx.disabled || _ctx.readonly || !_ctx.decreasable,
onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.decrease && _ctx.decrease(...args)))
}, null, 8 /* PROPS */, _hoisted_1))
: createCommentVNode("v-if", true),
createElementVNode("input", mergeProps({
ref: "input",
class: "vue-number-input__input"
}, _ctx.attrs, {
type: "number",
name: _ctx.name,
value: isNaN(_ctx.value) ? '' : _ctx.value,
min: _ctx.min,
max: _ctx.max,
step: _ctx.step,
readonly: _ctx.readonly || !_ctx.inputtable,
disabled: _ctx.disabled || (!_ctx.decreasable && !_ctx.increasable),
placeholder: _ctx.placeholder,
autocomplete: "off",
onChange: _cache[1] || (_cache[1] = (...args) => (_ctx.change && _ctx.change(...args))),
onPaste: _cache[2] || (_cache[2] = (...args) => (_ctx.paste && _ctx.paste(...args)))
}), null, 16 /* FULL_PROPS */, _hoisted_2),
(_ctx.controls)
? (openBlock(), createElementBlock("button", {
key: 1,
class: "vue-number-input__button vue-number-input__button--plus",
type: "button",
tabindex: "-1",
disabled: _ctx.disabled || _ctx.readonly || !_ctx.increasable,
onClick: _cache[3] || (_cache[3] = (...args) => (_ctx.increase && _ctx.increase(...args)))
}, null, 8 /* PROPS */, _hoisted_3))
: createCommentVNode("v-if", true)
], 2 /* CLASS */))
}
function styleInject(css, ref) {
if ( ref === void 0 ) ref = {};
var insertAt = ref.insertAt;
if (!css || typeof document === 'undefined') { return; }
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
if (insertAt === 'top') {
if (head.firstChild) {
head.insertBefore(style, head.firstChild);
} else {
head.appendChild(style);
}
} else {
head.appendChild(style);
}
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
}
var css_248z = ".vue-number-input[data-v-188efc8c]{display:block;font-size:0;max-width:100%;overflow:hidden;position:relative}.vue-number-input__button[data-v-188efc8c]{background-color:#fff;border:0;border-radius:.25rem;bottom:1px;position:absolute;top:1px;width:2.5rem;z-index:1}.vue-number-input__button[data-v-188efc8c]:focus{outline:none}.vue-number-input__button[data-v-188efc8c]:hover:after,.vue-number-input__button[data-v-188efc8c]:hover:before{background-color:#0074d9}.vue-number-input__button[data-v-188efc8c]:disabled{opacity:.65}.vue-number-input__button[data-v-188efc8c]:disabled:after,.vue-number-input__button[data-v-188efc8c]:disabled:before{background-color:#ddd}.vue-number-input__button[data-v-188efc8c]:after,.vue-number-input__button[data-v-188efc8c]:before{background-color:#111;content:\"\";left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);transition:background-color .15s}.vue-number-input__button[data-v-188efc8c]:before{height:1px;width:50%}.vue-number-input__button[data-v-188efc8c]:after{height:50%;width:1px}.vue-number-input__button--minus[data-v-188efc8c]{border-bottom-right-radius:0;border-right:1px solid #ddd;border-top-right-radius:0;left:1px}.vue-number-input__button--minus[data-v-188efc8c]:after{visibility:hidden}.vue-number-input__button--plus[data-v-188efc8c]{border-bottom-left-radius:0;border-left:1px solid #ddd;border-top-left-radius:0;right:1px}.vue-number-input__input[data-v-188efc8c]{-moz-appearance:textfield;background-color:#fff;border:1px solid #ddd;border-radius:.25rem;display:block;font-size:1rem;line-height:1.5;max-width:100%;min-height:1.5rem;min-width:3rem;padding:.4375rem .875rem;transition:border-color .15s;width:100%}.vue-number-input__input[data-v-188efc8c]::-webkit-inner-spin-button,.vue-number-input__input[data-v-188efc8c]::-webkit-outer-spin-button{-webkit-appearance:none}.vue-number-input__input[data-v-188efc8c]:focus{border-color:#0074d9;outline:none}.vue-number-input__input[data-v-188efc8c]:disabled,.vue-number-input__input[readonly][data-v-188efc8c]{background-color:#f8f8f8}.vue-number-input--inline[data-v-188efc8c]{display:inline-block}.vue-number-input--inline>input[data-v-188efc8c]{display:inline-block;width:12.5rem}.vue-number-input--center>input[data-v-188efc8c]{text-align:center}.vue-number-input--controls>input[data-v-188efc8c]{padding-left:3.375rem;padding-right:3.375rem}.vue-number-input--small>input[data-v-188efc8c]{border-radius:.1875rem;font-size:.875rem;padding:.25rem .5rem}.vue-number-input--small.vue-number-input--inline>input[data-v-188efc8c]{width:10rem}.vue-number-input--small.vue-number-input--controls>button[data-v-188efc8c]{width:2rem}.vue-number-input--small.vue-number-input--controls>input[data-v-188efc8c]{padding-left:2.5rem;padding-right:2.5rem}.vue-number-input--large>input[data-v-188efc8c]{border-radius:.3125rem;font-size:1.25rem;padding:.5rem 1rem}.vue-number-input--large.vue-number-input--inline>input[data-v-188efc8c]{width:15rem}.vue-number-input--large.vue-number-input--controls>button[data-v-188efc8c]{width:3rem}.vue-number-input--large.vue-number-input--controls>input[data-v-188efc8c]{padding-left:4rem;padding-right:4rem}";
styleInject(css_248z);
script.render = render;
script.__scopeId = "data-v-188efc8c";
export { script as default };