@inkline/inkline
Version:
Inkline is the Vue.js UI/UX Library built for creating your next design system
205 lines (196 loc) • 5.78 kB
text/typescript
import { defineComponent } from 'vue';
import IButton from '@inkline/inkline/components/IButton/index.vue';
import IInput from '@inkline/inkline/components/IInput/index.vue';
import { uid } from '@inkline/inkline/helpers';
import { defaultPropValue, sizePropValidator } from '@inkline/inkline/mixins/props';
import { InputElementEvent } from '@inkline/inkline/types';
const componentName = 'INumberInput';
export default defineComponent({
name: componentName,
components: {
IButton
},
extends: IInput,
props: {
/**
* The color variant of the input
* @type light | dark
* @default light
* @name color
*/
color: {
type: String,
default: defaultPropValue<string>(componentName, 'color')
},
/**
* Display the input as clearable
* @type Boolean
* @default false
* @name clearable
*/
clearable: {
type: Boolean,
default: false
},
/**
* The disabled state of the input
* @type Boolean
* @default false
* @name disabled
*/
disabled: {
type: Boolean,
default: false
},
/**
* The id of the internal input element
* @type String
* @default
* @name id
*/
id: {
type: String,
default: ''
},
/**
* Used to set the field value
* @type String | Number
* @default
* @name modelValue
*/
modelValue: {
type: [String, Number],
default: ''
},
/**
* The unique identifier of the input
* @type String
* @default uid()
* @name name
*/
name: {
type: [String, Number],
default (): string {
return uid('input');
}
},
/**
* The readonly state of the input
* @type Boolean
* @default false
* @name readonly
*/
readonly: {
type: Boolean,
default: false
},
/**
* The size variant of the input
* @type sm | md | lg
* @default md
* @name size
*/
size: {
type: String,
default: defaultPropValue<string>(componentName, 'size'),
validator: sizePropValidator
},
/**
* The tabindex of the input
* @type Number | String
* @default 0
* @name tabindex
*/
tabindex: {
type: [Number, String],
default: 0
},
/**
* The minimum allowed input value
* @type Number
* @default -Infinity
* @name min
*/
min: {
type: [Number, String],
default: -Infinity
},
/**
* The maximum allowed input value
* @type Number
* @default +Infinity
* @name max
*/
max: {
type: [Number, String],
default: Infinity
},
/**
* The precision of the input value, for floating point numbers
* @type Number
* @default 0
* @name precision
*/
precision: {
type: Number,
default: 0
},
/**
* The increment step to increase or decrease the value by
* @type Number
* @default 1
* @name step
*/
step: {
type: Number,
default: 1
}
},
emits: [
/**
* Event emitted for setting the modelValue
* @event update:modelValue
*/
'update:modelValue'
],
watch: {
modelValue: {
immediate: true,
handler (value: string) {
let newValue = (value || '').toString()
.replace(/^[^0-9-]/, '')
.replace(/^(-)[^0-9]/, '$1')
.replace(new RegExp(`^(-?[0-9]+)[^0-9${this.precision > 0 ? '.' : ''}]`), '$1');
if (this.precision > 0) {
newValue = newValue
.replace(/^(-?[0-9]+\.)[^0-9]/, '$1')
.replace(new RegExp(`^(-?[0-9]+\\.[0-9]{0,${this.precision}}).*`), '$1');
}
if (parseFloat(newValue) >= parseFloat(this.max as string)) newValue = this.max.toString();
if (parseFloat(newValue) <= parseFloat(this.min as string)) newValue = this.min.toString();
(this.parent as any).onInput?.(this.name, newValue);
this.$emit('update:modelValue', newValue);
}
}
},
methods: {
decrease () {
this.$emit('update:modelValue', this.formatPrecision((Number(this.modelValue) - this.step).toString()));
},
increase () {
this.$emit('update:modelValue', this.formatPrecision((Number(this.modelValue) + this.step).toString()));
},
formatPrecision (value: string) {
const parts = value.split('.');
let decimals = parts[1] || '';
for (let i = decimals.length; i < this.precision; i += 1) {
decimals += '0';
}
return this.precision > 0 ? `${parts[0]}.${decimals}` : parts[0];
},
onBlurFormatPrecision (event: InputElementEvent) {
this.$emit('update:modelValue', this.formatPrecision(Number(this.modelValue).toString()));
(this.parent as any).onBlur?.(this.name, event);
}
}
});