dm4t2 / intl-number-input

Easy input of formatted numbers based on the ECMAScript Internationalization API (Intl.NumberFormat).
https://dm4t2.github.io/intl-number-input/
MIT License
12 stars 3 forks source link

Use in component with Vue2 #10

Open kalitk0 opened 2 years ago

kalitk0 commented 2 years ago

Hi, how to use this as component with Vue 2?

dm4t2 commented 2 years ago

Hi, you can use NumberInput.vue as a reference. This is the demo component of the playground.

RiesvGeffen commented 2 years ago

Might not be that relevant to this specific issue, but for anyone trying to use this with Vue 3. I got it to work with the following composable:

const findInput = (el: HTMLElement | null) =>
  (el?.matches('input') ? el : el?.querySelector('input')) as HTMLInputElement

export const useNumericInput = (options: NumberInputOptions, autoEmit?: boolean) => {
  let numberInput: NumberInput | null
  const inputRef = ref<HTMLInputElement | ComponentPublicInstance | null>(null)
  const formattedValue = ref<string | null>(null)
  const numberValue = ref<number | null>(null)

  const vm = getCurrentInstance()
  const emit = vm?.emit || vm?.proxy?.$emit?.bind(vm?.proxy)
  const props = (vm?.props || vm?.proxy?.$props) as Record<string, unknown>
  const lazyModel = (vm?.attrs.modelModifiers as Record<string, boolean>)?.lazy
  const modelValue: ComputedRef<number | null> = computed(() => props?.modelValue as number)

  watch(inputRef, (value) => {
    if (value) {
      const el = findInput((value as ComponentPublicInstance)?.$el ?? value)
      if (el) {
        numberInput = new NumberInput({
          el,
          options,
          onInput: (value) => {
            if (!lazyModel && autoEmit !== false && modelValue.value !== value.number) {
              emit?.('update:modelValue', value.number)
            }
            numberValue.value = value.number
            formattedValue.value = value.formatted
          },
          onChange: (value) => {
            emit?.('update:modelValue', value.number)
          },
        })
        numberInput.setValue(modelValue.value)
      } else {
        console.error('No input element found. Please make sure that the "inputRef" template ref is properly assigned.')
      }
    } else {
      numberInput = null
    }
  })

  return {
    inputRef,
    numberValue,
    formattedValue,
    setValue: (value: number | null) => numberInput?.setValue(value),
    setOptions: (options: NumberInputOptions) => numberInput?.setOptions(options),
  }
}

I am using this composable in the following component:

<script setup lang="ts">
import { NumberInputOptions, NumberFormatStyle, CurrencyDisplay } from 'intl-number-input'
import { PropType } from 'vue'
import { useSettingsStore } from '@/modules/settings/stores/settings'

const { getRegion, getCurrency } = useSettingsStore()

const props = defineProps({
  modelValue: { type: Number, default: 0.0 },
  options: { type: Object as PropType<NumberInputOptions>, default: null },
})

const { inputRef } = useNumericInput({
  locale: getRegion,
  currency: getCurrency,
  formatStyle: NumberFormatStyle.Currency,
  currencyDisplay: CurrencyDisplay.Symbol,
  valueRange: {
    min: 0,
  },
  hidePrefixOrSuffixOnFocus: false,
  hideGroupingSeparatorOnFocus: false,
  hideNegligibleDecimalDigitsOnFocus: false,
  autoSign: true,
  useGrouping: true,
  ...props.options,
})
</script>

<template>
  <input ref="inputRef" type="text" />
</template>

You can call this component from anywhere, with the option to override any option:

<script setup lang="ts">
const value = ref(undefined)
</script>

<template>
  <SharedInputsCurrency v-model.lazy="value" :options="{ currency: 'GBP' }" />
</template>

Only thing I am still struggling with is using this on a Vuetify 3 <v-text-field />.


@dm4t2 would love to hear your feedback on why you did not create https://github.com/dm4t2/vue-currency-input like this. Since I am missing other numeric inputs (like percent) in that project.

dm4t2 commented 2 years ago

@RiesvGeffen Vue Currency Input is only about inputting currency formatted numbers (like the name says). Intl Number Input on the other hand is a framework-agnostic and generalized implementation.

See also https://github.com/dm4t2/vue-currency-input/discussions/270