# Componente

# Introducción

Este componente renderizará un input de tipo numeric field. El mismo se compone de dos partes, una de renderizado y otra de lógica (IrNumericField Bone), esta ultima será la encargada de el manejo de los datos.

# Uso

Es importante recalcar el nombre del componente IrNumericField a nivel de código, ya que este deberá usarse para llamar al componente para renderizarlo, ya sea individiualmente o en un IrForm.

# Ejemplo

# Propiedades

Propiedad Tipo Default Descripción
readonly Boolean

false

El valor readonly = true, establecerá al campo como solo lectura.

hint String

``

Establece un texto de ayuda para el campo.
value Number

Numero ingresado en el componente.

required Boolean

false

En valor true, define que el componente debe ser valorizado para ser válido.

pattern String

''

Un String que defina una expresión regular que deberá cumplir el input para ser válido. En caso de no poderse enviar \ desde la capa de datos, enviese ~, ICL lo tomará como \ en su lugar.

max Number

undefined

Número máximo que se puede ingresar. Al no estar setteada esta propiedad el límite es infinito.

min Number

undefined

Número mínimo que se puede ingresar. Al no estar setteada esta propiedad el límite es infinito.

hint Object | String

''

Genera un texto de ayuda para el componente, que podrá ser o no, clickeable para navegar a un link determinado

Propiedad con estructura de objeto con los siguientes keys:

{
  link: String,
  text: String
  openNewTab:Boolean
  classes:String
}

En la propiedad text del objeto se determinará el texto que mostrará el hint, esta propiedad debe estar valorizada para que se muestre el hint.

En la propiedad link del objeto se determinará el link al cual navegará al clickear en el hint, si no se encuentra valorizada el hint no será clickeable, solo será texto.

En la propiedad openNewTab del objeto se determinará si al clickearse el hint con link valorizado, se navegará en una nueva pestaña. Solo es útil paracuando la propedad link del objeto está definida. Si no se encuentra definido, se navegará en la misma pestaña.

En la propiedad classes del objeto, se determinarán las clases que se aplicarán al hint, las clases serán separadas por un espacio, sirven para definir estilos. Se utilizarán principalmente clases de vuetify.

Las clases aplicables para las filas de la tabla podrán encontrarse en Vuetify en los siguientes enlaces:

mask String
  ''

Determina un formato que cumplirá el texto introducido en el campo. Por ejemplo siendo mask = "999-999" el valor ingresado se forzará a ser algo como 333-333. El patron se define con los siguientes 3 caracteres: 9 para números, A para letras y S para alfanuméricos. El resto de los caracteres ingresados será tomado como tal.

money Object
  {}

Determina un formato que cumplirá el valor introducido en el campo. Este formato será definido por:

{
  // Decimal precision -> "90"
  precision: 2,
  // Decimal separator -> ",90"
  separator: ',',
  // Thousand delimiter -> "12.345.678"
  delimiter: '.',
  // Money unit -> "R$ 12.345.678,90"
  unit: 'R$',
  // Money unit -> "12.345.678,90 R$"
  suffixUnit: 'R$',
  // Force type only number instead decimal,
  // masking decimals with ",00"
  // Zero cents -> "R$ 1.234.567.890,00"
  zeroCents: true
}

# Código fuente

<template>
  <ir-numeric-field-bone
    v-bind="$attrs"
    :value="value"
    @input="handleInputIRT"
  >
    <div
      slot-scope="{
        inputEvents,
        placeholder,
        readonly,
        rules,
        hideDetails,
        label,
        comportamientos,
        comportamientosPositions,
        comportamientoEvents,
        id_resolver,
        maskHandler
      }"
      class="input-group js-parent-focus tw-w-full"
    >
      <ir-label
        v-if="isOutlined"
        :label="label"
        :error="hasError"
        :disabled="handledAttrs.disabled"
        :readonly="readonly"
      />
      <v-text-field
        :ref="irNumericFieldRef"
        :value="value"
        type="number"
        name="number"
        class="pt-0 align-center"
        :label="isOutlined ? undefined : label"
        v-bind="getFilteredProps"
        :placeholder="placeholder"
        :hide-details="hideDetails"
        :rules="rules"
        :readonly="readonly"
        :disabled="readonly || handledAttrs.disabled"
        @input="(e)=>{ handleInputIRT(e), maskHandler(e, $refs[irNumericFieldRef]) }"
        @update:error="setError"
        @blur="handleInput"
        v-on="inputEvents"
      >
        <template #message="{ message, key }">
          <div v-html="message" />
        </template>
        <template
          v-for="(position, k) in comportamientosPositions"
          #[position]
        >
          <div
            :key="'slot-comportamientos-numeric-' + k"
            class="d-flex align-center"
            style="gap: 10px"
          >
            <template v-for="(comportamiento, i) in comportamientos">
              <ir-comportamiento
                v-if="
                  (typeof comportamiento.visible === 'undefined' ||
                    comportamiento.visible) &&
                  comportamiento.position === position
                "
                :key="'comportamiento-numericfield-' + i"
                :father_component_name="numericFieldComponentName"
                :current_id_resolver="id_resolver"
                v-bind="comportamiento"
                :value="value"
                v-on="comportamientoEvents"
              />
            </template>
          </div>
        </template>
        <template
          v-if="readonly && !value"
          #prepend-inner
        >
          <div class="pr-2">
            <v-icon color="warning">mdi-alert-circle</v-icon>
          </div>
        </template>
      </v-text-field>
    </div>
  </ir-numeric-field-bone>
</template>

<script>
import IrNumericFieldBone from './IrNumericFieldBone.vue'
import { numericFieldComponentName } from '../../../constants'
import { setHintTag } from '../../../helpers/hint'
import focusAndScrollMixin from '../../../mixins/focusAndScroll'
import { isOutlined } from '../../../helpers/utils.js'
import { filterProps } from '../../../helpers/filterProps'

export default {
  name: numericFieldComponentName,
  components: {
    IrNumericFieldBone
  },
  mixins: [focusAndScrollMixin],
  inheritAttrs: false,
  props: {
    value: {
      type: undefined, //Indefinido por caso en que se borre el contenido del input, se maneja en vuetify como "", rompería el type number
      default: 0,
      required: true
    },
    isFocused: {
      type: Boolean,
      default: false
    },
    hint: {
      type: [String, Object],
      default: ''
    }
  },
  data() {
    return {
      irNumericFieldRef: numericFieldComponentName,
      otherProps: {},
      numericFieldComponentName: numericFieldComponentName,
      hasError: false
    }
  },
  computed: {
    getFilteredProps() {
      return filterProps(this.$refs[this.irNumericFieldRef], this.handledAttrs, [
        'data-cy',
        'max',
        'min'
      ])
    },
    isOutlined() {
      return isOutlined(this.handledAttrs.outlined)
    },
    handledAttrs() {
      const hint = setHintTag(this.otherProps, this.hint)
      let aux = { ...this.otherProps, ...this.$attrs }
      delete aux.hint
      if (hint) {
        aux = { ...aux, hint }
      }
      delete aux.placeholder
      delete aux.readonly
      delete aux.rules
      delete aux.pattern
      delete aux.label
      delete aux.value
      delete aux.type
      delete aux.name
      return aux
    }
  },
  mounted() {
    if (this.isFocused) {
      let element =
        this.$refs[this.irNumericFieldRef].$el.querySelector('input')
      this.focusAndScroll(element)
    }
    this.otherProps = { ...this.$attrs }
  },
  methods: {
    setError(eventValue) {
      this.hasError = eventValue
    },
    /**
     * Handles the input event.
     *
     * @param {number|string} newValue - The new value to be handled.
     */
    handleInputIRT(newValue) {
      const minValue = this.$attrs.min
      const isPositive = minValue >= 0
      this.updateValue(newValue, isPositive && newValue < 0)
    },
    /**
     * Handles the input event.
     *
     * @param {Event} newValue - The new value to be handled.
     */
    handleInput(newValue) {
      const minValue = this.$attrs.min
      let newLocalValue = newValue.target.value
      this.updateValue(newLocalValue, newLocalValue < minValue)
    },
    /**
     * Updates the component's value with validation.
     *
     * @param {number|string} newLocalValue - The new value to be updated.
     * @param {boolean} validationLocal - The validation result indicating if the new value is valid.
     */
    updateValue(newLocalValue, validationLocal) {
      if (isNaN(newLocalValue)) {
        this.$emit('input', this.value)
        return
      }
      if (newLocalValue !== this.value) {
        const minValue = this.$attrs.min
        const maxValue = this.$attrs.max
        const value = isNaN(parseFloat(newLocalValue)) ? '' : parseFloat(newLocalValue)
        const isHigher = value > maxValue
        if (maxValue !== undefined && isHigher) {
          this.$emit('input', maxValue)
          return
        }
        if (minValue !== undefined && validationLocal) {
          this.$emit('input', minValue)
          return
        }
        this.$emit('input', value)
      }
    }
  }
}
</script>

<style scoped>
.btn-reg {
  border-bottom-left-radius: 0 !important;
  border-top-left-radius: 0 !important;
}
</style>
Last Updated: 7/25/2025, 7:28:07 PM