running-tools

A collection of tools for runners and their coaches
git clone https://git.ashermorgan.net/running-tools/
Log | Files | Refs | README

DecimalInput.vue (1988B)


      1 <template>
      2   <input ref="inputElement" type="number" step="any" required @blur="onblur" v-model="stringValue">
      3 </template>
      4 
      5 <script setup lang="ts">
      6 import { ref, watch } from 'vue';
      7 import { formatNumber } from '@/core/units';
      8 
      9 /**
     10  * The component value
     11  */
     12 const model = defineModel({
     13   type: Number,
     14   default: 0,
     15 });
     16 
     17 const props = defineProps({
     18   /**
     19    * The number of digits to show before the decimal point
     20    */
     21   padding: {
     22     type: Number,
     23     default: 0,
     24     validator(value: number) {
     25       return value >= 0;
     26     },
     27   },
     28 
     29   /**
     30    * The number of digits to show after the decimal point
     31    */
     32   digits: {
     33     type: Number,
     34     default: 1,
     35     validator(value: number) {
     36       return value > 0;
     37     },
     38   },
     39 });
     40 
     41 /**
     42  * The internal float value
     43  */
     44 const internalValue = ref(model.value);
     45 
     46 /**
     47  * The raw string value (empty if input is currently invalid)
     48  */
     49 const stringValue = ref(format(model.value));
     50 
     51 /**
     52  * The input element
     53  */
     54 const inputElement = ref();
     55 
     56 /*
     57  * Update the internal value when the component value changes
     58  */
     59 watch(model, (newValue) => {
     60   if (Math.abs(newValue - internalValue.value) > 0.00001) {
     61     internalValue.value = newValue;
     62     stringValue.value = format(internalValue.value);
     63   }
     64 });
     65 
     66 /**
     67  * Update the internal value when the raw string value changes
     68  */
     69 watch(stringValue, (newValue) => {
     70   if (inputElement.value.validity.valid) {
     71     internalValue.value = Number(newValue);
     72     model.value = internalValue.value;
     73   }
     74 });
     75 
     76 /**
     77  * Reformat display value if not invalid
     78  */
     79 function onblur() {
     80   if (inputElement.value.validity.valid) {
     81     stringValue.value = format(internalValue.value);
     82   }
     83 }
     84 
     85 /**
     86  * Format a decimal as a string
     87  * @param {number} value The decimal
     88  * @returns {string} The formated string
     89  */
     90 function format(value: number): string {
     91   return formatNumber(value, props.padding, props.digits, true);
     92 }
     93 </script>
     94 
     95 <style scoped>
     96 input {
     97   width: 5em;  /* can fit 999.99 comfortably */
     98   text-align: center;
     99 }
    100 </style>