JSFiddle - React, Tailwind, and code Playground
by Константин Дралюк
HTML
<div id="app">
<div>min: 0, max: 99, step: 1</div>
<ui-number v-model="firstNumberValue" :min="0" :max="99"></ui-number>
<div>min: 0, max: 50, step: 0.1</div>
<ui-number v-model="secondNumberValue" :min="0" :max="50" :mantissa="1"></ui-number>
<div>min: 1, max: 2, step: 0.01</div>
<ui-number v-model="thirdNumberValue" :min="1" :max="2" :mantissa="2" :disabled='true'></ui-number>
</div>
<script type="text/x-template" id="ui-number">
<div class="ui-number">
<button @click="stepDown" :disabled="disabled">-</button>
<input
ref="number"
type="text"
v-bind="$attrs"
v-on="$listeners"
:value="value"
@input="input"
@blur="blur"
@keydown.down="stepDown"
@keydown.up="stepUp"
:disabled="disabled"
/>
<button @click="stepUp" :disabled="disabled">+</button>
</div>
</script>
SCSS
.ui-number, .ui-number * {
box-sizing: border-box;
}
.ui-number {
display: inline-flex;
border-radius: 4px;
overflow: hidden;
&__input {
border-radius: 0;
outline: none;
border: 1px solid #000;
width: 100px;
padding: 0 5px;
height: 30px;
}
&__control {
appearance: none;
display: flex;
align-items: center;
justify-content: center;
width: 30px;
border: none;
background: #000;
color: #fff;
}
}
Vue
Vue.component('ui-number', {
template: '#ui-number',
props: {
min: {
type: Number,
default: Number.MIN_VALUE
},
max: {
type: Number,
default: Number.MAX_VALUE
},
mantissa: {
type: Number,
default: 0
},
disabled: {
type: Boolean,
default: false
},
value: {
default: 0
}
},
computed: {
step () {
if (!this.mantissa) return 1;
const zs = []
zs.length = this.mantissa - 1
zs.fill(0)
return Number('.' + zs.join('') + 1)
}
},
methods: {
input (e) {
this.$nextTick(() => {
const value = e.target.value
const formatted = this.format(value)
const keepValue = value == formatted && this.mantissa && value.includes('.')
this.$emit('input', keepValue ? value : formatted)
})
},
blur (e) {
this.$nextTick(() => {
this.$emit('input', this.format(e.target.value))
})
},
stepDown () {
let value = Number(this.value) - this.step
if (value < this.min) value = this.min
this.$emit('input', this.format(value))
},
stepUp () {
let value = Number(this.value) + this.step
if (value > this.max) value = this.max
this.$emit('input', this.format(value))
},
format (value) {
if (value < this.min) value = this.min
if (value > this.max) value = this.max
const float = parseFloat(String(value).replace(/\D^.^-/g, '')).toFixed(this.mantissa)
return Number(float) || 0
}
}
})
new Vue({
el: '#app',
data: () => ({
firstNumberValue: 0,
secondNumberValue: 0,
thirdNumberValue: 1,
})
})