JSFiddle - React, Tailwind, and code Playground

by skirtle

HTML

<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<div id="app">
  <arabic-number-input v-model="phoneNumber"></arabic-number-input>
</div>

<p>
  Select text and drag/drop into the input:
</p>
<p>
  0123456789
</p>
<p>
  abc123def
</p>

JavaScript

const ArabicNumberInput = {
  template: `
    <input
      ref="input"
      v-model="inputValue"
      @keypress="onKeyPress"
		>
  `,
  
  props: ['value'],
  
  computed: {
    inputValue: {
      get () {
        return this.value
      },
      set () {
        this.setInputValue(...this.getInputValue().map(this.toArabicNumerals))
      }
    }
  },
  
  methods: {
    getInputValue() {
      const input = this.$refs.input
      const value = input.value
      const start = input.selectionStart
      const end = input.selectionEnd

      return [
        value.slice(0, start),   // before selection
        value.slice(start, end), // selection
        value.slice(end)         // after selection
      ]
    },
  
    onKeyPress(ev) {
      ev.preventDefault()

      const [before, , after] = this.getInputValue()
      const keyValue = this.toArabicNumerals(ev.key)

      this.setInputValue(before + keyValue, '', after)
    },
    
    setInputValue(before, selection, after) {
      const input = this.$refs.input
      const start = before.length
      const end = start + selection.length

      const value = input.value = before + selection + after

      input.setSelectionRange(start, end)
      
      this.$emit('update', value)
    },

    toArabicNumerals(str) {
      return str.split('').map(chr => {
        if ('0' <= chr && chr <= '9') {
          return String.fromCharCode(+chr + 1632)
        }

        if ('\u0660' <= chr && chr <= '\u0669') {
          return chr
        }

        return ''
      }).join('')
    }
  }
}

new Vue({
  el: '#app',

  components: {
  	ArabicNumberInput
  },

  data() {
    return {
      phoneNumber: ''
    }
  }
})